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