]>
Commit | Line | Data |
---|---|---|
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::HasAttrs; | |
12 | use feature_gate::{feature_err, EXPLAIN_STMT_ATTR_SYNTAX, Features, get_features, GateIssue}; | |
13 | use {fold, attr}; | |
14 | use ast; | |
15 | use codemap::Spanned; | |
16 | use parse::{token, ParseSess}; | |
17 | use syntax_pos::Span; | |
18 | ||
19 | use ptr::P; | |
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 | pub should_test: bool, | |
25 | pub sess: &'a ParseSess, | |
26 | pub features: Option<&'a Features>, | |
27 | } | |
28 | ||
29 | // `cfg_attr`-process the crate's attributes and compute the crate's features. | |
30 | pub fn features(mut krate: ast::Crate, sess: &ParseSess, should_test: bool) | |
31 | -> (ast::Crate, Features) { | |
32 | let features; | |
33 | { | |
34 | let mut strip_unconfigured = StripUnconfigured { | |
35 | should_test: should_test, | |
36 | sess: sess, | |
37 | features: None, | |
38 | }; | |
39 | ||
40 | let unconfigured_attrs = krate.attrs.clone(); | |
41 | let err_count = sess.span_diagnostic.err_count(); | |
42 | if let Some(attrs) = strip_unconfigured.configure(krate.attrs) { | |
43 | krate.attrs = attrs; | |
44 | } else { // the entire crate is unconfigured | |
45 | krate.attrs = Vec::new(); | |
46 | krate.module.items = Vec::new(); | |
47 | return (krate, Features::new()); | |
48 | } | |
49 | ||
50 | features = get_features(&sess.span_diagnostic, &krate.attrs); | |
51 | ||
52 | // Avoid reconfiguring malformed `cfg_attr`s | |
53 | if err_count == sess.span_diagnostic.err_count() { | |
54 | strip_unconfigured.features = Some(&features); | |
55 | strip_unconfigured.configure(unconfigured_attrs); | |
56 | } | |
57 | } | |
58 | ||
59 | (krate, features) | |
60 | } | |
61 | ||
62 | macro_rules! configure { | |
63 | ($this:ident, $node:ident) => { | |
64 | match $this.configure($node) { | |
65 | Some(node) => node, | |
66 | None => return Default::default(), | |
67 | } | |
68 | } | |
69 | } | |
70 | ||
71 | impl<'a> StripUnconfigured<'a> { | |
72 | pub fn configure<T: HasAttrs>(&mut self, node: T) -> Option<T> { | |
73 | let node = self.process_cfg_attrs(node); | |
74 | if self.in_cfg(node.attrs()) { Some(node) } else { None } | |
75 | } | |
76 | ||
77 | pub fn process_cfg_attrs<T: HasAttrs>(&mut self, node: T) -> T { | |
78 | node.map_attrs(|attrs| { | |
79 | attrs.into_iter().filter_map(|attr| self.process_cfg_attr(attr)).collect() | |
80 | }) | |
81 | } | |
82 | ||
83 | fn process_cfg_attr(&mut self, attr: ast::Attribute) -> Option<ast::Attribute> { | |
84 | if !attr.check_name("cfg_attr") { | |
85 | return Some(attr); | |
86 | } | |
87 | ||
88 | let (cfg, path, tokens, span) = match attr.parse(self.sess, |parser| { | |
89 | parser.expect(&token::OpenDelim(token::Paren))?; | |
90 | let cfg = parser.parse_meta_item()?; | |
91 | parser.expect(&token::Comma)?; | |
92 | let lo = parser.span.lo; | |
93 | let (path, tokens) = parser.parse_path_and_tokens()?; | |
94 | parser.expect(&token::CloseDelim(token::Paren))?; | |
95 | Ok((cfg, path, tokens, Span { lo: lo, ..parser.prev_span })) | |
96 | }) { | |
97 | Ok(result) => result, | |
98 | Err(mut e) => { | |
99 | e.emit(); | |
100 | return None; | |
101 | } | |
102 | }; | |
103 | ||
104 | if attr::cfg_matches(&cfg, self.sess, self.features) { | |
105 | self.process_cfg_attr(ast::Attribute { | |
106 | id: attr::mk_attr_id(), | |
107 | style: attr.style, | |
108 | path: path, | |
109 | tokens: tokens, | |
110 | is_sugared_doc: false, | |
111 | span: span, | |
112 | }) | |
113 | } else { | |
114 | None | |
115 | } | |
116 | } | |
117 | ||
118 | // Determine if a node with the given attributes should be included in this configuation. | |
119 | pub fn in_cfg(&mut self, attrs: &[ast::Attribute]) -> bool { | |
120 | attrs.iter().all(|attr| { | |
121 | // When not compiling with --test we should not compile the #[test] functions | |
122 | if !self.should_test && is_test_or_bench(attr) { | |
123 | return false; | |
124 | } | |
125 | ||
126 | let mis = if !is_cfg(attr) { | |
127 | return true; | |
128 | } else if let Some(mis) = attr.meta_item_list() { | |
129 | mis | |
130 | } else { | |
131 | return true; | |
132 | }; | |
133 | ||
134 | if mis.len() != 1 { | |
135 | self.sess.span_diagnostic.span_err(attr.span, "expected 1 cfg-pattern"); | |
136 | return true; | |
137 | } | |
138 | ||
139 | if !mis[0].is_meta_item() { | |
140 | self.sess.span_diagnostic.span_err(mis[0].span, "unexpected literal"); | |
141 | return true; | |
142 | } | |
143 | ||
144 | attr::cfg_matches(mis[0].meta_item().unwrap(), self.sess, self.features) | |
145 | }) | |
146 | } | |
147 | ||
148 | // Visit attributes on expression and statements (but not attributes on items in blocks). | |
149 | fn visit_expr_attrs(&mut self, attrs: &[ast::Attribute]) { | |
150 | // flag the offending attributes | |
151 | for attr in attrs.iter() { | |
152 | if !self.features.map(|features| features.stmt_expr_attributes).unwrap_or(true) { | |
153 | let mut err = feature_err(self.sess, | |
154 | "stmt_expr_attributes", | |
155 | attr.span, | |
156 | GateIssue::Language, | |
157 | EXPLAIN_STMT_ATTR_SYNTAX); | |
158 | if attr.is_sugared_doc { | |
159 | err.help("`///` is for documentation comments. For a plain comment, use `//`."); | |
160 | } | |
161 | err.emit(); | |
162 | } | |
163 | } | |
164 | } | |
165 | ||
166 | pub fn configure_foreign_mod(&mut self, foreign_mod: ast::ForeignMod) -> ast::ForeignMod { | |
167 | ast::ForeignMod { | |
168 | abi: foreign_mod.abi, | |
169 | items: foreign_mod.items.into_iter().filter_map(|item| self.configure(item)).collect(), | |
170 | } | |
171 | } | |
172 | ||
173 | fn configure_variant_data(&mut self, vdata: ast::VariantData) -> ast::VariantData { | |
174 | match vdata { | |
175 | ast::VariantData::Struct(fields, id) => { | |
176 | let fields = fields.into_iter().filter_map(|field| self.configure(field)); | |
177 | ast::VariantData::Struct(fields.collect(), id) | |
178 | } | |
179 | ast::VariantData::Tuple(fields, id) => { | |
180 | let fields = fields.into_iter().filter_map(|field| self.configure(field)); | |
181 | ast::VariantData::Tuple(fields.collect(), id) | |
182 | } | |
183 | ast::VariantData::Unit(id) => ast::VariantData::Unit(id) | |
184 | } | |
185 | } | |
186 | ||
187 | pub fn configure_item_kind(&mut self, item: ast::ItemKind) -> ast::ItemKind { | |
188 | match item { | |
189 | ast::ItemKind::Struct(def, generics) => { | |
190 | ast::ItemKind::Struct(self.configure_variant_data(def), generics) | |
191 | } | |
192 | ast::ItemKind::Union(def, generics) => { | |
193 | ast::ItemKind::Union(self.configure_variant_data(def), generics) | |
194 | } | |
195 | ast::ItemKind::Enum(def, generics) => { | |
196 | let variants = def.variants.into_iter().filter_map(|v| { | |
197 | self.configure(v).map(|v| { | |
198 | Spanned { | |
199 | node: ast::Variant_ { | |
200 | name: v.node.name, | |
201 | attrs: v.node.attrs, | |
202 | data: self.configure_variant_data(v.node.data), | |
203 | disr_expr: v.node.disr_expr, | |
204 | }, | |
205 | span: v.span | |
206 | } | |
207 | }) | |
208 | }); | |
209 | ast::ItemKind::Enum(ast::EnumDef { | |
210 | variants: variants.collect(), | |
211 | }, generics) | |
212 | } | |
213 | item => item, | |
214 | } | |
215 | } | |
216 | ||
217 | pub fn configure_expr_kind(&mut self, expr_kind: ast::ExprKind) -> ast::ExprKind { | |
218 | match expr_kind { | |
219 | ast::ExprKind::Match(m, arms) => { | |
220 | let arms = arms.into_iter().filter_map(|a| self.configure(a)).collect(); | |
221 | ast::ExprKind::Match(m, arms) | |
222 | } | |
223 | ast::ExprKind::Struct(path, fields, base) => { | |
224 | let fields = fields.into_iter() | |
225 | .filter_map(|field| { | |
226 | self.configure(field) | |
227 | }) | |
228 | .collect(); | |
229 | ast::ExprKind::Struct(path, fields, base) | |
230 | } | |
231 | _ => expr_kind, | |
232 | } | |
233 | } | |
234 | ||
235 | pub fn configure_expr(&mut self, expr: P<ast::Expr>) -> P<ast::Expr> { | |
236 | self.visit_expr_attrs(expr.attrs()); | |
237 | ||
238 | // If an expr is valid to cfg away it will have been removed by the | |
239 | // outer stmt or expression folder before descending in here. | |
240 | // Anything else is always required, and thus has to error out | |
241 | // in case of a cfg attr. | |
242 | // | |
243 | // NB: This is intentionally not part of the fold_expr() function | |
244 | // in order for fold_opt_expr() to be able to avoid this check | |
245 | if let Some(attr) = expr.attrs().iter().find(|a| is_cfg(a) || is_test_or_bench(a)) { | |
246 | let msg = "removing an expression is not supported in this position"; | |
247 | self.sess.span_diagnostic.span_err(attr.span, msg); | |
248 | } | |
249 | ||
250 | self.process_cfg_attrs(expr) | |
251 | } | |
252 | ||
253 | pub fn configure_stmt(&mut self, stmt: ast::Stmt) -> Option<ast::Stmt> { | |
254 | self.configure(stmt) | |
255 | } | |
256 | ||
257 | pub fn configure_struct_expr_field(&mut self, field: ast::Field) -> Option<ast::Field> { | |
258 | self.configure(field) | |
259 | } | |
260 | ||
261 | pub fn configure_pat(&mut self, pattern: P<ast::Pat>) -> P<ast::Pat> { | |
262 | pattern.map(|mut pattern| { | |
263 | if let ast::PatKind::Struct(path, fields, etc) = pattern.node { | |
264 | let fields = fields.into_iter() | |
265 | .filter_map(|field| { | |
266 | self.configure(field) | |
267 | }) | |
268 | .collect(); | |
269 | pattern.node = ast::PatKind::Struct(path, fields, etc); | |
270 | } | |
271 | pattern | |
272 | }) | |
273 | } | |
274 | } | |
275 | ||
276 | impl<'a> fold::Folder for StripUnconfigured<'a> { | |
277 | fn fold_foreign_mod(&mut self, foreign_mod: ast::ForeignMod) -> ast::ForeignMod { | |
278 | let foreign_mod = self.configure_foreign_mod(foreign_mod); | |
279 | fold::noop_fold_foreign_mod(foreign_mod, self) | |
280 | } | |
281 | ||
282 | fn fold_item_kind(&mut self, item: ast::ItemKind) -> ast::ItemKind { | |
283 | let item = self.configure_item_kind(item); | |
284 | fold::noop_fold_item_kind(item, self) | |
285 | } | |
286 | ||
287 | fn fold_expr(&mut self, expr: P<ast::Expr>) -> P<ast::Expr> { | |
288 | let mut expr = self.configure_expr(expr).unwrap(); | |
289 | expr.node = self.configure_expr_kind(expr.node); | |
290 | P(fold::noop_fold_expr(expr, self)) | |
291 | } | |
292 | ||
293 | fn fold_opt_expr(&mut self, expr: P<ast::Expr>) -> Option<P<ast::Expr>> { | |
294 | let mut expr = configure!(self, expr).unwrap(); | |
295 | expr.node = self.configure_expr_kind(expr.node); | |
296 | Some(P(fold::noop_fold_expr(expr, self))) | |
297 | } | |
298 | ||
299 | fn fold_stmt(&mut self, stmt: ast::Stmt) -> SmallVector<ast::Stmt> { | |
300 | match self.configure_stmt(stmt) { | |
301 | Some(stmt) => fold::noop_fold_stmt(stmt, self), | |
302 | None => return SmallVector::new(), | |
303 | } | |
304 | } | |
305 | ||
306 | fn fold_item(&mut self, item: P<ast::Item>) -> SmallVector<P<ast::Item>> { | |
307 | fold::noop_fold_item(configure!(self, item), self) | |
308 | } | |
309 | ||
310 | fn fold_impl_item(&mut self, item: ast::ImplItem) -> SmallVector<ast::ImplItem> { | |
311 | fold::noop_fold_impl_item(configure!(self, item), self) | |
312 | } | |
313 | ||
314 | fn fold_trait_item(&mut self, item: ast::TraitItem) -> SmallVector<ast::TraitItem> { | |
315 | fold::noop_fold_trait_item(configure!(self, item), self) | |
316 | } | |
317 | ||
318 | fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac { | |
319 | // Don't configure interpolated AST (c.f. #34171). | |
320 | // Interpolated AST will get configured once the surrounding tokens are parsed. | |
321 | mac | |
322 | } | |
323 | ||
324 | fn fold_pat(&mut self, pattern: P<ast::Pat>) -> P<ast::Pat> { | |
325 | fold::noop_fold_pat(self.configure_pat(pattern), self) | |
326 | } | |
327 | } | |
328 | ||
329 | fn is_cfg(attr: &ast::Attribute) -> bool { | |
330 | attr.check_name("cfg") | |
331 | } | |
332 | ||
333 | pub fn is_test_or_bench(attr: &ast::Attribute) -> bool { | |
334 | attr.check_name("test") || attr.check_name("bench") | |
335 | } |