4 EXPLAIN_STMT_ATTR_SYNTAX,
11 use source_map::Spanned;
13 use parse::{token, ParseSess};
14 use smallvec::SmallVec;
15 use errors::Applicability;
16 use util::move_map::MoveMap;
20 /// A folder that strips out items that do not belong in the current configuration.
21 pub struct StripUnconfigured<'a> {
22 pub sess: &'a ParseSess,
23 pub features: Option<&'a Features>,
26 // `cfg_attr`-process the crate's attributes and compute the crate's features.
27 pub fn features(mut krate: ast::Crate, sess: &ParseSess, edition: Edition)
28 -> (ast::Crate, Features) {
31 let mut strip_unconfigured = StripUnconfigured {
36 let unconfigured_attrs = krate.attrs.clone();
37 let err_count = sess.span_diagnostic.err_count();
38 if let Some(attrs) = strip_unconfigured.configure(krate.attrs) {
40 } else { // the entire crate is unconfigured
41 krate.attrs = Vec::new();
42 krate.module.items = Vec::new();
43 return (krate, Features::new());
46 features = get_features(&sess.span_diagnostic, &krate.attrs, edition);
48 // Avoid reconfiguring malformed `cfg_attr`s
49 if err_count == sess.span_diagnostic.err_count() {
50 strip_unconfigured.features = Some(&features);
51 strip_unconfigured.configure(unconfigured_attrs);
58 macro_rules! configure {
59 ($this:ident, $node:ident) => {
60 match $this.configure($node) {
62 None => return Default::default(),
67 impl<'a> StripUnconfigured<'a> {
68 pub fn configure<T: HasAttrs>(&mut self, node: T) -> Option<T> {
69 let node = self.process_cfg_attrs(node);
70 if self.in_cfg(node.attrs()) { Some(node) } else { None }
73 /// Parse and expand all `cfg_attr` attributes into a list of attributes
74 /// that are within each `cfg_attr` that has a true configuration predicate.
76 /// Gives compiler warnigns if any `cfg_attr` does not contain any
77 /// attributes and is in the original source code. Gives compiler errors if
78 /// the syntax of any `cfg_attr` is incorrect.
79 pub fn process_cfg_attrs<T: HasAttrs>(&mut self, node: T) -> T {
80 node.map_attrs(|attrs| {
81 attrs.into_iter().flat_map(|attr| self.process_cfg_attr(attr)).collect()
85 /// Parse and expand a single `cfg_attr` attribute into a list of attributes
86 /// when the configuration predicate is true, or otherwise expand into an
87 /// empty list of attributes.
89 /// Gives a compiler warning when the `cfg_attr` contains no attributes and
90 /// is in the original source file. Gives a compiler error if the syntax of
91 /// the attribute is incorrect
92 fn process_cfg_attr(&mut self, attr: ast::Attribute) -> Vec<ast::Attribute> {
93 if !attr.check_name("cfg_attr") {
97 let (cfg_predicate, expanded_attrs) = match attr.parse(self.sess, |parser| {
98 parser.expect(&token::OpenDelim(token::Paren))?;
100 let cfg_predicate = parser.parse_meta_item()?;
101 parser.expect(&token::Comma)?;
103 // Presumably, the majority of the time there will only be one attr.
104 let mut expanded_attrs = Vec::with_capacity(1);
106 while !parser.check(&token::CloseDelim(token::Paren)) {
107 let lo = parser.span.lo();
108 let (path, tokens) = parser.parse_meta_item_unrestricted()?;
109 expanded_attrs.push((path, tokens, parser.prev_span.with_lo(lo)));
110 parser.expect_one_of(&[token::Comma], &[token::CloseDelim(token::Paren)])?;
113 parser.expect(&token::CloseDelim(token::Paren))?;
114 Ok((cfg_predicate, expanded_attrs))
116 Ok(result) => result,
123 // Check feature gate and lint on zero attributes in source. Even if the feature is gated,
124 // we still compute as if it wasn't, since the emitted error will stop compilation further
125 // along the compilation.
126 if expanded_attrs.len() == 0 {
127 // FIXME: Emit unused attribute lint here.
130 if attr::cfg_matches(&cfg_predicate, self.sess, self.features) {
131 // We call `process_cfg_attr` recursively in case there's a
132 // `cfg_attr` inside of another `cfg_attr`. E.g.
133 // `#[cfg_attr(false, cfg_attr(true, some_attr))]`.
134 expanded_attrs.into_iter()
135 .flat_map(|(path, tokens, span)| self.process_cfg_attr(ast::Attribute {
136 id: attr::mk_attr_id(),
140 is_sugared_doc: false,
149 /// Determine if a node with the given attributes should be included in this configuration.
150 pub fn in_cfg(&mut self, attrs: &[ast::Attribute]) -> bool {
151 attrs.iter().all(|attr| {
156 let error = |span, msg, suggestion: &str| {
157 let mut err = self.sess.span_diagnostic.struct_span_err(span, msg);
158 if !suggestion.is_empty() {
161 "expected syntax is",
163 Applicability::MaybeIncorrect,
170 let meta_item = match attr.parse_meta(self.sess) {
171 Ok(meta_item) => meta_item,
172 Err(mut err) => { err.emit(); return true; }
174 let nested_meta_items = if let Some(nested_meta_items) = meta_item.meta_item_list() {
177 return error(meta_item.span, "`cfg` is not followed by parentheses",
178 "cfg(/* predicate */)");
181 if nested_meta_items.is_empty() {
182 return error(meta_item.span, "`cfg` predicate is not specified", "");
183 } else if nested_meta_items.len() > 1 {
184 return error(nested_meta_items.last().unwrap().span,
185 "multiple `cfg` predicates are specified", "");
188 match nested_meta_items[0].meta_item() {
189 Some(meta_item) => attr::cfg_matches(meta_item, self.sess, self.features),
190 None => error(nested_meta_items[0].span,
191 "`cfg` predicate key cannot be a literal", ""),
196 /// Visit attributes on expression and statements (but not attributes on items in blocks).
197 fn visit_expr_attrs(&mut self, attrs: &[ast::Attribute]) {
198 // flag the offending attributes
199 for attr in attrs.iter() {
200 self.maybe_emit_expr_attr_err(attr);
204 /// If attributes are not allowed on expressions, emit an error for `attr`
205 pub fn maybe_emit_expr_attr_err(&self, attr: &ast::Attribute) {
206 if !self.features.map(|features| features.stmt_expr_attributes).unwrap_or(true) {
207 let mut err = feature_err(self.sess,
208 "stmt_expr_attributes",
211 EXPLAIN_STMT_ATTR_SYNTAX);
213 if attr.is_sugared_doc {
214 err.help("`///` is for documentation comments. For a plain comment, use `//`.");
221 pub fn configure_foreign_mod(&mut self, foreign_mod: ast::ForeignMod) -> ast::ForeignMod {
223 abi: foreign_mod.abi,
224 items: foreign_mod.items.move_flat_map(|item| self.configure(item)),
228 fn configure_variant_data(&mut self, vdata: ast::VariantData) -> ast::VariantData {
230 ast::VariantData::Struct(fields, id) => {
231 let fields = fields.move_flat_map(|field| self.configure(field));
232 ast::VariantData::Struct(fields, id)
234 ast::VariantData::Tuple(fields, id) => {
235 let fields = fields.move_flat_map(|field| self.configure(field));
236 ast::VariantData::Tuple(fields, id)
238 ast::VariantData::Unit(id) => ast::VariantData::Unit(id)
242 pub fn configure_item_kind(&mut self, item: ast::ItemKind) -> ast::ItemKind {
244 ast::ItemKind::Struct(def, generics) => {
245 ast::ItemKind::Struct(self.configure_variant_data(def), generics)
247 ast::ItemKind::Union(def, generics) => {
248 ast::ItemKind::Union(self.configure_variant_data(def), generics)
250 ast::ItemKind::Enum(def, generics) => {
251 let variants = def.variants.move_flat_map(|v| {
252 self.configure(v).map(|v| {
254 node: ast::Variant_ {
257 data: self.configure_variant_data(v.node.data),
258 disr_expr: v.node.disr_expr,
264 ast::ItemKind::Enum(ast::EnumDef { variants }, generics)
270 pub fn configure_expr_kind(&mut self, expr_kind: ast::ExprKind) -> ast::ExprKind {
272 ast::ExprKind::Match(m, arms) => {
273 let arms = arms.move_flat_map(|a| self.configure(a));
274 ast::ExprKind::Match(m, arms)
276 ast::ExprKind::Struct(path, fields, base) => {
277 let fields = fields.move_flat_map(|field| self.configure(field));
278 ast::ExprKind::Struct(path, fields, base)
284 pub fn configure_expr(&mut self, expr: P<ast::Expr>) -> P<ast::Expr> {
285 self.visit_expr_attrs(expr.attrs());
287 // If an expr is valid to cfg away it will have been removed by the
288 // outer stmt or expression folder before descending in here.
289 // Anything else is always required, and thus has to error out
290 // in case of a cfg attr.
292 // N.B., this is intentionally not part of the fold_expr() function
293 // in order for fold_opt_expr() to be able to avoid this check
294 if let Some(attr) = expr.attrs().iter().find(|a| is_cfg(a)) {
295 let msg = "removing an expression is not supported in this position";
296 self.sess.span_diagnostic.span_err(attr.span, msg);
299 self.process_cfg_attrs(expr)
302 pub fn configure_pat(&mut self, pattern: P<ast::Pat>) -> P<ast::Pat> {
303 pattern.map(|mut pattern| {
304 if let ast::PatKind::Struct(path, fields, etc) = pattern.node {
305 let fields = fields.move_flat_map(|field| self.configure(field));
306 pattern.node = ast::PatKind::Struct(path, fields, etc);
312 // deny #[cfg] on generic parameters until we decide what to do with it.
314 pub fn disallow_cfg_on_generic_param(&mut self, param: &ast::GenericParam) {
315 for attr in param.attrs() {
316 let offending_attr = if attr.check_name("cfg") {
318 } else if attr.check_name("cfg_attr") {
323 let msg = format!("#[{}] cannot be applied on a generic parameter", offending_attr);
324 self.sess.span_diagnostic.span_err(attr.span, &msg);
329 impl<'a> fold::Folder for StripUnconfigured<'a> {
330 fn fold_foreign_mod(&mut self, foreign_mod: ast::ForeignMod) -> ast::ForeignMod {
331 let foreign_mod = self.configure_foreign_mod(foreign_mod);
332 fold::noop_fold_foreign_mod(foreign_mod, self)
335 fn fold_item_kind(&mut self, item: ast::ItemKind) -> ast::ItemKind {
336 let item = self.configure_item_kind(item);
337 fold::noop_fold_item_kind(item, self)
340 fn fold_expr(&mut self, expr: P<ast::Expr>) -> P<ast::Expr> {
341 let mut expr = self.configure_expr(expr).into_inner();
342 expr.node = self.configure_expr_kind(expr.node);
343 P(fold::noop_fold_expr(expr, self))
346 fn fold_opt_expr(&mut self, expr: P<ast::Expr>) -> Option<P<ast::Expr>> {
347 let mut expr = configure!(self, expr).into_inner();
348 expr.node = self.configure_expr_kind(expr.node);
349 Some(P(fold::noop_fold_expr(expr, self)))
352 fn fold_stmt(&mut self, stmt: ast::Stmt) -> SmallVec<[ast::Stmt; 1]> {
353 fold::noop_fold_stmt(configure!(self, stmt), self)
356 fn fold_item(&mut self, item: P<ast::Item>) -> SmallVec<[P<ast::Item>; 1]> {
357 fold::noop_fold_item(configure!(self, item), self)
360 fn fold_impl_item(&mut self, item: ast::ImplItem) -> SmallVec<[ast::ImplItem; 1]>
362 fold::noop_fold_impl_item(configure!(self, item), self)
365 fn fold_trait_item(&mut self, item: ast::TraitItem) -> SmallVec<[ast::TraitItem; 1]> {
366 fold::noop_fold_trait_item(configure!(self, item), self)
369 fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac {
370 // Don't configure interpolated AST (cf. issue #34171).
371 // Interpolated AST will get configured once the surrounding tokens are parsed.
375 fn fold_pat(&mut self, pattern: P<ast::Pat>) -> P<ast::Pat> {
376 fold::noop_fold_pat(self.configure_pat(pattern), self)
380 fn is_cfg(attr: &ast::Attribute) -> bool {
381 attr.check_name("cfg")