|
9 | 9 | // except according to those terms.
|
10 | 10 |
|
11 | 11 | use attr::HasAttrs;
|
12 |
| -use feature_gate::{feature_err, EXPLAIN_STMT_ATTR_SYNTAX, Features, get_features, GateIssue}; |
| 12 | +use feature_gate::{ |
| 13 | + feature_err, |
| 14 | + EXPLAIN_STMT_ATTR_SYNTAX, |
| 15 | + Features, |
| 16 | + get_features, |
| 17 | + GateIssue, |
| 18 | + emit_feature_err, |
| 19 | +}; |
13 | 20 | use {fold, attr};
|
14 | 21 | use ast;
|
15 | 22 | use source_map::Spanned;
|
@@ -73,49 +80,103 @@ impl<'a> StripUnconfigured<'a> {
|
73 | 80 | if self.in_cfg(node.attrs()) { Some(node) } else { None }
|
74 | 81 | }
|
75 | 82 |
|
| 83 | + /// Parse and expand all `cfg_attr` attributes into a list of attributes |
| 84 | + /// that are within each `cfg_attr` that has a true configuration predicate. |
| 85 | + /// |
| 86 | + /// Gives compiler warnigns if any `cfg_attr` does not contain any |
| 87 | + /// attributes and is in the original source code. Gives compiler errors if |
| 88 | + /// the syntax of any `cfg_attr` is incorrect. |
76 | 89 | pub fn process_cfg_attrs<T: HasAttrs>(&mut self, node: T) -> T {
|
77 | 90 | node.map_attrs(|attrs| {
|
78 |
| - attrs.into_iter().filter_map(|attr| self.process_cfg_attr(attr)).collect() |
| 91 | + attrs.into_iter().flat_map(|attr| self.process_cfg_attr(attr)).collect() |
79 | 92 | })
|
80 | 93 | }
|
81 | 94 |
|
82 |
| - fn process_cfg_attr(&mut self, attr: ast::Attribute) -> Option<ast::Attribute> { |
| 95 | + /// Parse and expand a single `cfg_attr` attribute into a list of attributes |
| 96 | + /// when the configuration predicate is true, or otherwise expand into an |
| 97 | + /// empty list of attributes. |
| 98 | + /// |
| 99 | + /// Gives a compiler warning when the `cfg_attr` contains no attribtes and |
| 100 | + /// is in the original source file. Gives a compiler error if the syntax of |
| 101 | + /// the attribute is incorrect |
| 102 | + fn process_cfg_attr(&mut self, attr: ast::Attribute) -> Vec<ast::Attribute> { |
83 | 103 | if !attr.check_name("cfg_attr") {
|
84 |
| - return Some(attr); |
| 104 | + return vec![attr]; |
85 | 105 | }
|
86 | 106 |
|
87 |
| - let (cfg, path, tokens, span) = match attr.parse(self.sess, |parser| { |
| 107 | + let gate_cfg_attr_multi = if let Some(ref features) = self.features { |
| 108 | + !features.cfg_attr_multi |
| 109 | + } else { |
| 110 | + false |
| 111 | + }; |
| 112 | + let cfg_attr_span = attr.span; |
| 113 | + |
| 114 | + let (cfg_predicate, expanded_attrs) = match attr.parse(self.sess, |parser| { |
88 | 115 | parser.expect(&token::OpenDelim(token::Paren))?;
|
89 |
| - let cfg = parser.parse_meta_item()?; |
| 116 | + |
| 117 | + let cfg_predicate = parser.parse_meta_item()?; |
90 | 118 | parser.expect(&token::Comma)?;
|
91 |
| - let lo = parser.span.lo(); |
92 |
| - let (path, tokens) = parser.parse_meta_item_unrestricted()?; |
93 |
| - parser.eat(&token::Comma); // Optional trailing comma |
| 119 | + |
| 120 | + // Presumably, the majority of the time there will only be one attr. |
| 121 | + let mut expanded_attrs = Vec::with_capacity(1); |
| 122 | + |
| 123 | + while !parser.check(&token::CloseDelim(token::Paren)) { |
| 124 | + let lo = parser.span.lo(); |
| 125 | + let (path, tokens) = parser.parse_meta_item_unrestricted()?; |
| 126 | + expanded_attrs.push((path, tokens, parser.prev_span.with_lo(lo))); |
| 127 | + parser.expect_one_of(&[token::Comma], &[token::CloseDelim(token::Paren)])?; |
| 128 | + } |
| 129 | + |
94 | 130 | parser.expect(&token::CloseDelim(token::Paren))?;
|
95 |
| - Ok((cfg, path, tokens, parser.prev_span.with_lo(lo))) |
| 131 | + Ok((cfg_predicate, expanded_attrs)) |
96 | 132 | }) {
|
97 | 133 | Ok(result) => result,
|
98 | 134 | Err(mut e) => {
|
99 | 135 | e.emit();
|
100 |
| - return None; |
| 136 | + return Vec::new(); |
101 | 137 | }
|
102 | 138 | };
|
103 | 139 |
|
104 |
| - if attr::cfg_matches(&cfg, self.sess, self.features) { |
105 |
| - self.process_cfg_attr(ast::Attribute { |
| 140 | + // Check feature gate and lint on zero attributes in source. Even if the feature is gated, |
| 141 | + // we still compute as if it wasn't, since the emitted error will stop compilation futher |
| 142 | + // along the compilation. |
| 143 | + match (expanded_attrs.len(), gate_cfg_attr_multi) { |
| 144 | + (0, false) => { |
| 145 | + // FIXME: Emit unused attribute lint here. |
| 146 | + }, |
| 147 | + (1, _) => {}, |
| 148 | + (_, true) => { |
| 149 | + emit_feature_err( |
| 150 | + self.sess, |
| 151 | + "cfg_attr_multi", |
| 152 | + cfg_attr_span, |
| 153 | + GateIssue::Language, |
| 154 | + "cfg_attr with zero or more than one attributes is experimental", |
| 155 | + ); |
| 156 | + }, |
| 157 | + (_, false) => {} |
| 158 | + } |
| 159 | + |
| 160 | + if attr::cfg_matches(&cfg_predicate, self.sess, self.features) { |
| 161 | + // We call `process_cfg_attr` recursively in case there's a |
| 162 | + // `cfg_attr` inside of another `cfg_attr`. E.g. |
| 163 | + // `#[cfg_attr(false, cfg_attr(true, some_attr))]`. |
| 164 | + expanded_attrs.into_iter() |
| 165 | + .flat_map(|(path, tokens, span)| self.process_cfg_attr(ast::Attribute { |
106 | 166 | id: attr::mk_attr_id(),
|
107 | 167 | style: attr.style,
|
108 | 168 | path,
|
109 | 169 | tokens,
|
110 | 170 | is_sugared_doc: false,
|
111 | 171 | span,
|
112 |
| - }) |
| 172 | + })) |
| 173 | + .collect() |
113 | 174 | } else {
|
114 |
| - None |
| 175 | + Vec::new() |
115 | 176 | }
|
116 | 177 | }
|
117 | 178 |
|
118 |
| - // Determine if a node with the given attributes should be included in this configuration. |
| 179 | + /// Determine if a node with the given attributes should be included in this configuration. |
119 | 180 | pub fn in_cfg(&mut self, attrs: &[ast::Attribute]) -> bool {
|
120 | 181 | attrs.iter().all(|attr| {
|
121 | 182 | if !is_cfg(attr) {
|
@@ -165,7 +226,7 @@ impl<'a> StripUnconfigured<'a> {
|
165 | 226 | })
|
166 | 227 | }
|
167 | 228 |
|
168 |
| - // Visit attributes on expression and statements (but not attributes on items in blocks). |
| 229 | + /// Visit attributes on expression and statements (but not attributes on items in blocks). |
169 | 230 | fn visit_expr_attrs(&mut self, attrs: &[ast::Attribute]) {
|
170 | 231 | // flag the offending attributes
|
171 | 232 | for attr in attrs.iter() {
|
|
0 commit comments