]>
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 | ||
9e0c209e | 11 | use attr::HasAttrs; |
c30ab7b3 | 12 | use feature_gate::{feature_err, EXPLAIN_STMT_ATTR_SYNTAX, Features, get_features, GateIssue}; |
3157f602 XL |
13 | use {fold, attr}; |
14 | use ast; | |
476ff2be | 15 | use codemap::Spanned; |
cc61c64b XL |
16 | use parse::{token, ParseSess}; |
17 | use syntax_pos::Span; | |
1a4d82fc | 18 | |
cc61c64b | 19 | use ptr::P; |
1a4d82fc JJ |
20 | use util::small_vector::SmallVector; |
21 | ||
3157f602 XL |
22 | /// A folder that strips out items that do not belong in the current configuration. |
23 | pub struct StripUnconfigured<'a> { | |
3157f602 XL |
24 | pub should_test: bool, |
25 | pub sess: &'a ParseSess, | |
26 | pub features: Option<&'a Features>, | |
1a4d82fc JJ |
27 | } |
28 | ||
9e0c209e SL |
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 { | |
9e0c209e SL |
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 | ||
3157f602 | 71 | impl<'a> StripUnconfigured<'a> { |
9e0c209e | 72 | pub fn configure<T: HasAttrs>(&mut self, node: T) -> Option<T> { |
3157f602 XL |
73 | let node = self.process_cfg_attrs(node); |
74 | if self.in_cfg(node.attrs()) { Some(node) } else { None } | |
1a4d82fc | 75 | } |
1a4d82fc | 76 | |
5bcae85e | 77 | pub fn process_cfg_attrs<T: HasAttrs>(&mut self, node: T) -> T { |
3157f602 XL |
78 | node.map_attrs(|attrs| { |
79 | attrs.into_iter().filter_map(|attr| self.process_cfg_attr(attr)).collect() | |
80 | }) | |
1a4d82fc | 81 | } |
85aaf69f | 82 | |
3157f602 | 83 | fn process_cfg_attr(&mut self, attr: ast::Attribute) -> Option<ast::Attribute> { |
85aaf69f | 84 | if !attr.check_name("cfg_attr") { |
3157f602 | 85 | return Some(attr); |
85aaf69f SL |
86 | } |
87 | ||
cc61c64b XL |
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(); | |
d9579d0f AL |
100 | return None; |
101 | } | |
102 | }; | |
9e0c209e | 103 | |
cc61c64b XL |
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 | |
85aaf69f SL |
115 | } |
116 | } | |
117 | ||
3157f602 | 118 | // Determine if a node with the given attributes should be included in this configuation. |
9e0c209e | 119 | pub fn in_cfg(&mut self, attrs: &[ast::Attribute]) -> bool { |
3157f602 XL |
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 | } | |
92a42be0 | 125 | |
7cac9316 | 126 | let mis = if !is_cfg(attr) { |
cc61c64b XL |
127 | return true; |
128 | } else if let Some(mis) = attr.meta_item_list() { | |
129 | mis | |
130 | } else { | |
131 | return true; | |
3157f602 | 132 | }; |
92a42be0 | 133 | |
3157f602 XL |
134 | if mis.len() != 1 { |
135 | self.sess.span_diagnostic.span_err(attr.span, "expected 1 cfg-pattern"); | |
136 | return true; | |
92a42be0 SL |
137 | } |
138 | ||
9e0c209e SL |
139 | if !mis[0].is_meta_item() { |
140 | self.sess.span_diagnostic.span_err(mis[0].span, "unexpected literal"); | |
141 | return true; | |
142 | } | |
143 | ||
c30ab7b3 | 144 | attr::cfg_matches(mis[0].meta_item().unwrap(), self.sess, self.features) |
3157f602 XL |
145 | }) |
146 | } | |
92a42be0 | 147 | |
3157f602 | 148 | // Visit attributes on expression and statements (but not attributes on items in blocks). |
9e0c209e | 149 | fn visit_expr_attrs(&mut self, attrs: &[ast::Attribute]) { |
3157f602 XL |
150 | // flag the offending attributes |
151 | for attr in attrs.iter() { | |
152 | if !self.features.map(|features| features.stmt_expr_attributes).unwrap_or(true) { | |
7cac9316 | 153 | let mut err = feature_err(self.sess, |
c30ab7b3 SL |
154 | "stmt_expr_attributes", |
155 | attr.span, | |
156 | GateIssue::Language, | |
157 | EXPLAIN_STMT_ATTR_SYNTAX); | |
476ff2be | 158 | if attr.is_sugared_doc { |
c30ab7b3 SL |
159 | err.help("`///` is for documentation comments. For a plain comment, use `//`."); |
160 | } | |
161 | err.emit(); | |
92a42be0 | 162 | } |
92a42be0 SL |
163 | } |
164 | } | |
92a42be0 | 165 | |
9e0c209e | 166 | pub fn configure_foreign_mod(&mut self, foreign_mod: ast::ForeignMod) -> ast::ForeignMod { |
3157f602 XL |
167 | ast::ForeignMod { |
168 | abi: foreign_mod.abi, | |
9e0c209e | 169 | items: foreign_mod.items.into_iter().filter_map(|item| self.configure(item)).collect(), |
92a42be0 SL |
170 | } |
171 | } | |
172 | ||
9e0c209e SL |
173 | fn configure_variant_data(&mut self, vdata: ast::VariantData) -> ast::VariantData { |
174 | match vdata { | |
3157f602 | 175 | ast::VariantData::Struct(fields, id) => { |
9e0c209e | 176 | let fields = fields.into_iter().filter_map(|field| self.configure(field)); |
3157f602 XL |
177 | ast::VariantData::Struct(fields.collect(), id) |
178 | } | |
179 | ast::VariantData::Tuple(fields, id) => { | |
9e0c209e | 180 | let fields = fields.into_iter().filter_map(|field| self.configure(field)); |
3157f602 XL |
181 | ast::VariantData::Tuple(fields.collect(), id) |
182 | } | |
183 | ast::VariantData::Unit(id) => ast::VariantData::Unit(id) | |
9e0c209e SL |
184 | } |
185 | } | |
3157f602 | 186 | |
9e0c209e SL |
187 | pub fn configure_item_kind(&mut self, item: ast::ItemKind) -> ast::ItemKind { |
188 | match item { | |
3157f602 | 189 | ast::ItemKind::Struct(def, generics) => { |
9e0c209e SL |
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) | |
3157f602 XL |
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, | |
9e0c209e | 202 | data: self.configure_variant_data(v.node.data), |
3157f602 XL |
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, | |
9e0c209e SL |
214 | } |
215 | } | |
3157f602 | 216 | |
9e0c209e | 217 | pub fn configure_expr_kind(&mut self, expr_kind: ast::ExprKind) -> ast::ExprKind { |
32a655c1 SL |
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| { | |
32a655c1 SL |
226 | self.configure(field) |
227 | }) | |
228 | .collect(); | |
229 | ast::ExprKind::Struct(path, fields, base) | |
230 | } | |
231 | _ => expr_kind, | |
9e0c209e | 232 | } |
92a42be0 SL |
233 | } |
234 | ||
9e0c209e SL |
235 | pub fn configure_expr(&mut self, expr: P<ast::Expr>) -> P<ast::Expr> { |
236 | self.visit_expr_attrs(expr.attrs()); | |
3157f602 XL |
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); | |
92a42be0 | 248 | } |
3157f602 | 249 | |
9e0c209e | 250 | self.process_cfg_attrs(expr) |
92a42be0 SL |
251 | } |
252 | ||
9e0c209e SL |
253 | pub fn configure_stmt(&mut self, stmt: ast::Stmt) -> Option<ast::Stmt> { |
254 | self.configure(stmt) | |
92a42be0 | 255 | } |
32a655c1 SL |
256 | |
257 | pub fn configure_struct_expr_field(&mut self, field: ast::Field) -> Option<ast::Field> { | |
32a655c1 SL |
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| { | |
32a655c1 SL |
266 | self.configure(field) |
267 | }) | |
268 | .collect(); | |
269 | pattern.node = ast::PatKind::Struct(path, fields, etc); | |
270 | } | |
271 | pattern | |
272 | }) | |
273 | } | |
9e0c209e | 274 | } |
92a42be0 | 275 | |
9e0c209e SL |
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) | |
92a42be0 SL |
280 | } |
281 | ||
9e0c209e SL |
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), | |
476ff2be | 302 | None => return SmallVector::new(), |
9e0c209e | 303 | } |
92a42be0 | 304 | } |
92a42be0 | 305 | |
3157f602 | 306 | fn fold_item(&mut self, item: P<ast::Item>) -> SmallVector<P<ast::Item>> { |
9e0c209e | 307 | fold::noop_fold_item(configure!(self, item), self) |
3157f602 | 308 | } |
92a42be0 | 309 | |
3157f602 | 310 | fn fold_impl_item(&mut self, item: ast::ImplItem) -> SmallVector<ast::ImplItem> { |
9e0c209e | 311 | fold::noop_fold_impl_item(configure!(self, item), self) |
3157f602 | 312 | } |
92a42be0 | 313 | |
3157f602 | 314 | fn fold_trait_item(&mut self, item: ast::TraitItem) -> SmallVector<ast::TraitItem> { |
9e0c209e | 315 | fold::noop_fold_trait_item(configure!(self, item), self) |
92a42be0 | 316 | } |
3157f602 | 317 | |
9e0c209e | 318 | fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac { |
3157f602 XL |
319 | // Don't configure interpolated AST (c.f. #34171). |
320 | // Interpolated AST will get configured once the surrounding tokens are parsed. | |
9e0c209e | 321 | mac |
92a42be0 | 322 | } |
32a655c1 SL |
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 | } | |
92a42be0 SL |
327 | } |
328 | ||
3157f602 XL |
329 | fn is_cfg(attr: &ast::Attribute) -> bool { |
330 | attr.check_name("cfg") | |
92a42be0 SL |
331 | } |
332 | ||
c30ab7b3 | 333 | pub fn is_test_or_bench(attr: &ast::Attribute) -> bool { |
3157f602 | 334 | attr.check_name("test") || attr.check_name("bench") |
92a42be0 | 335 | } |