]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/config.rs
Auto merge of #34216 - jseyfried:nested_cfg_attr, r=nrc
[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, HasAttrs};
12 use errors::Handler;
13 use feature_gate::GatedCfgAttr;
14 use fold::Folder;
15 use {ast, fold, attr};
16 use codemap::{Spanned, respan};
17 use parse::token;
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 configuration.
23 pub struct StripUnconfigured<'a> {
24     diag: CfgDiagReal<'a, 'a>,
25     should_test: bool,
26     config: &'a ast::CrateConfig,
27 }
28
29 impl<'a> StripUnconfigured<'a> {
30     pub fn new(config: &'a ast::CrateConfig,
31                should_test: bool,
32                diagnostic: &'a Handler,
33                feature_gated_cfgs: &'a mut Vec<GatedCfgAttr>)
34                -> Self {
35         StripUnconfigured {
36             config: config,
37             should_test: should_test,
38             diag: CfgDiagReal { diag: diagnostic, feature_gated_cfgs: feature_gated_cfgs },
39         }
40     }
41
42     fn configure<T: HasAttrs>(&mut self, node: T) -> Option<T> {
43         let node = self.process_cfg_attrs(node);
44         if self.in_cfg(node.attrs()) { Some(node) } else { None }
45     }
46
47     fn process_cfg_attrs<T: HasAttrs>(&mut self, node: T) -> T {
48         node.map_attrs(|attrs| {
49             attrs.into_iter().filter_map(|attr| self.process_cfg_attr(attr)).collect()
50         })
51     }
52
53     fn process_cfg_attr(&mut self, attr: ast::Attribute) -> Option<ast::Attribute> {
54         if !attr.check_name("cfg_attr") {
55             return Some(attr);
56         }
57
58         let attr_list = match attr.meta_item_list() {
59             Some(attr_list) => attr_list,
60             None => {
61                 let msg = "expected `#[cfg_attr(<cfg pattern>, <attr>)]`";
62                 self.diag.diag.span_err(attr.span, msg);
63                 return None;
64             }
65         };
66         let (cfg, mi) = match (attr_list.len(), attr_list.get(0), attr_list.get(1)) {
67             (2, Some(cfg), Some(mi)) => (cfg, mi),
68             _ => {
69                 let msg = "expected `#[cfg_attr(<cfg pattern>, <attr>)]`";
70                 self.diag.diag.span_err(attr.span, msg);
71                 return None;
72             }
73         };
74
75         if attr::cfg_matches(self.config, &cfg, &mut self.diag) {
76             self.process_cfg_attr(respan(mi.span, ast::Attribute_ {
77                 id: attr::mk_attr_id(),
78                 style: attr.node.style,
79                 value: mi.clone(),
80                 is_sugared_doc: false,
81             }))
82         } else {
83             None
84         }
85     }
86
87     // Determine if a node with the given attributes should be included in this configuation.
88     fn in_cfg(&mut self, attrs: &[ast::Attribute]) -> bool {
89         attrs.iter().all(|attr| {
90             // When not compiling with --test we should not compile the #[test] functions
91             if !self.should_test && is_test_or_bench(attr) {
92                 return false;
93             }
94
95             let mis = match attr.node.value.node {
96                 ast::MetaItemKind::List(_, ref mis) if is_cfg(&attr) => mis,
97                 _ => return true
98             };
99
100             if mis.len() != 1 {
101                 self.diag.emit_error(|diagnostic| {
102                     diagnostic.span_err(attr.span, "expected 1 cfg-pattern");
103                 });
104                 return true;
105             }
106
107             attr::cfg_matches(self.config, &mis[0], &mut self.diag)
108         })
109     }
110
111     // Visit attributes on expression and statements (but not attributes on items in blocks).
112     fn visit_stmt_or_expr_attrs(&mut self, attrs: &[ast::Attribute]) {
113         // flag the offending attributes
114         for attr in attrs.iter() {
115             self.diag.feature_gated_cfgs.push(GatedCfgAttr::GatedAttr(attr.span));
116         }
117     }
118
119     // Visit unremovable (non-optional) expressions -- c.f. `fold_expr` vs `fold_opt_expr`.
120     fn visit_unremovable_expr(&mut self, expr: &ast::Expr) {
121         if let Some(attr) = expr.attrs().iter().find(|a| is_cfg(a) || is_test_or_bench(a)) {
122             let msg = "removing an expression is not supported in this position";
123             self.diag.diag.span_err(attr.span, msg);
124         }
125     }
126 }
127
128 // Support conditional compilation by transforming the AST, stripping out
129 // any items that do not belong in the current configuration
130 pub fn strip_unconfigured_items(diagnostic: &Handler, krate: ast::Crate, should_test: bool,
131                                 feature_gated_cfgs: &mut Vec<GatedCfgAttr>)
132                                 -> ast::Crate
133 {
134     let config = &krate.config.clone();
135     StripUnconfigured::new(config, should_test, diagnostic, feature_gated_cfgs).fold_crate(krate)
136 }
137
138 impl<'a> fold::Folder for StripUnconfigured<'a> {
139     fn fold_foreign_mod(&mut self, foreign_mod: ast::ForeignMod) -> ast::ForeignMod {
140         ast::ForeignMod {
141             abi: foreign_mod.abi,
142             items: foreign_mod.items.into_iter().filter_map(|item| {
143                 self.configure(item).map(|item| fold::noop_fold_foreign_item(item, self))
144             }).collect(),
145         }
146     }
147
148     fn fold_item_kind(&mut self, item: ast::ItemKind) -> ast::ItemKind {
149         let fold_struct = |this: &mut Self, vdata| match vdata {
150             ast::VariantData::Struct(fields, id) => {
151                 let fields = fields.into_iter().filter_map(|field| this.configure(field));
152                 ast::VariantData::Struct(fields.collect(), id)
153             }
154             ast::VariantData::Tuple(fields, id) => {
155                 let fields = fields.into_iter().filter_map(|field| this.configure(field));
156                 ast::VariantData::Tuple(fields.collect(), id)
157             }
158             ast::VariantData::Unit(id) => ast::VariantData::Unit(id)
159         };
160
161         let item = match item {
162             ast::ItemKind::Struct(def, generics) => {
163                 ast::ItemKind::Struct(fold_struct(self, def), generics)
164             }
165             ast::ItemKind::Enum(def, generics) => {
166                 let variants = def.variants.into_iter().filter_map(|v| {
167                     self.configure(v).map(|v| {
168                         Spanned {
169                             node: ast::Variant_ {
170                                 name: v.node.name,
171                                 attrs: v.node.attrs,
172                                 data: fold_struct(self, v.node.data),
173                                 disr_expr: v.node.disr_expr,
174                             },
175                             span: v.span
176                         }
177                     })
178                 });
179                 ast::ItemKind::Enum(ast::EnumDef {
180                     variants: variants.collect(),
181                 }, generics)
182             }
183             item => item,
184         };
185
186         fold::noop_fold_item_kind(item, self)
187     }
188
189     fn fold_expr(&mut self, expr: P<ast::Expr>) -> P<ast::Expr> {
190         self.visit_stmt_or_expr_attrs(expr.attrs());
191         // If an expr is valid to cfg away it will have been removed by the
192         // outer stmt or expression folder before descending in here.
193         // Anything else is always required, and thus has to error out
194         // in case of a cfg attr.
195         //
196         // NB: This is intentionally not part of the fold_expr() function
197         //     in order for fold_opt_expr() to be able to avoid this check
198         self.visit_unremovable_expr(&expr);
199         let expr = self.process_cfg_attrs(expr);
200         fold_expr(self, expr)
201     }
202
203     fn fold_opt_expr(&mut self, expr: P<ast::Expr>) -> Option<P<ast::Expr>> {
204         self.configure(expr).map(|expr| fold_expr(self, expr))
205     }
206
207     fn fold_stmt(&mut self, stmt: ast::Stmt) -> SmallVector<ast::Stmt> {
208         let is_item = match stmt.node {
209             ast::StmtKind::Decl(ref decl, _) => match decl.node {
210                 ast::DeclKind::Item(_) => true,
211                 _ => false,
212             },
213             _ => false,
214         };
215
216         // avoid calling `visit_stmt_or_expr_attrs` on items
217         if !is_item {
218             self.visit_stmt_or_expr_attrs(stmt.attrs());
219         }
220
221         self.configure(stmt).map(|stmt| fold::noop_fold_stmt(stmt, self))
222                             .unwrap_or(SmallVector::zero())
223     }
224
225     fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac {
226         fold::noop_fold_mac(mac, self)
227     }
228
229     fn fold_item(&mut self, item: P<ast::Item>) -> SmallVector<P<ast::Item>> {
230         self.configure(item).map(|item| fold::noop_fold_item(item, self))
231                             .unwrap_or(SmallVector::zero())
232     }
233
234     fn fold_impl_item(&mut self, item: ast::ImplItem) -> SmallVector<ast::ImplItem> {
235         self.configure(item).map(|item| fold::noop_fold_impl_item(item, self))
236                             .unwrap_or(SmallVector::zero())
237     }
238
239     fn fold_trait_item(&mut self, item: ast::TraitItem) -> SmallVector<ast::TraitItem> {
240         self.configure(item).map(|item| fold::noop_fold_trait_item(item, self))
241                             .unwrap_or(SmallVector::zero())
242     }
243
244     fn fold_interpolated(&mut self, nt: token::Nonterminal) -> token::Nonterminal {
245         // Don't configure interpolated AST (c.f. #34171).
246         // Interpolated AST will get configured once the surrounding tokens are parsed.
247         nt
248     }
249 }
250
251 fn fold_expr(folder: &mut StripUnconfigured, expr: P<ast::Expr>) -> P<ast::Expr> {
252     expr.map(|ast::Expr {id, span, node, attrs}| {
253         fold::noop_fold_expr(ast::Expr {
254             id: id,
255             node: match node {
256                 ast::ExprKind::Match(m, arms) => {
257                     ast::ExprKind::Match(m, arms.into_iter()
258                                         .filter_map(|a| folder.configure(a))
259                                         .collect())
260                 }
261                 _ => node
262             },
263             span: span,
264             attrs: attrs,
265         }, folder)
266     })
267 }
268
269 fn is_cfg(attr: &ast::Attribute) -> bool {
270     attr.check_name("cfg")
271 }
272
273 fn is_test_or_bench(attr: &ast::Attribute) -> bool {
274     attr.check_name("test") || attr.check_name("bench")
275 }
276
277 pub trait CfgDiag {
278     fn emit_error<F>(&mut self, f: F) where F: FnMut(&Handler);
279     fn flag_gated<F>(&mut self, f: F) where F: FnMut(&mut Vec<GatedCfgAttr>);
280 }
281
282 pub struct CfgDiagReal<'a, 'b> {
283     pub diag: &'a Handler,
284     pub feature_gated_cfgs: &'b mut Vec<GatedCfgAttr>,
285 }
286
287 impl<'a, 'b> CfgDiag for CfgDiagReal<'a, 'b> {
288     fn emit_error<F>(&mut self, mut f: F) where F: FnMut(&Handler) {
289         f(self.diag)
290     }
291     fn flag_gated<F>(&mut self, mut f: F) where F: FnMut(&mut Vec<GatedCfgAttr>) {
292         f(self.feature_gated_cfgs)
293     }
294 }