]>
Commit | Line | Data |
---|---|---|
ba9703b0 XL |
1 | //! Conditional compilation stripping. |
2 | ||
74b04a01 | 3 | use rustc_ast::ptr::P; |
29967ef6 | 4 | use rustc_ast::token::{DelimToken, Token, TokenKind}; |
cdc7bbd5 XL |
5 | use rustc_ast::tokenstream::{AttrAnnotatedTokenStream, AttrAnnotatedTokenTree}; |
6 | use rustc_ast::tokenstream::{DelimSpan, Spacing}; | |
7 | use rustc_ast::tokenstream::{LazyTokenStream, TokenTree}; | |
94222f64 | 8 | use rustc_ast::{self as ast, AstLike, AttrStyle, Attribute, MetaItem}; |
74b04a01 | 9 | use rustc_attr as attr; |
dfeec247 | 10 | use rustc_data_structures::fx::FxHashMap; |
ba9703b0 | 11 | use rustc_data_structures::map_in_place::MapInPlace; |
dfeec247 XL |
12 | use rustc_errors::{error_code, struct_span_err, Applicability, Handler}; |
13 | use rustc_feature::{Feature, Features, State as FeatureState}; | |
14 | use rustc_feature::{ | |
15 | ACCEPTED_FEATURES, ACTIVE_FEATURES, REMOVED_FEATURES, STABLE_REMOVED_FEATURES, | |
16 | }; | |
94222f64 | 17 | use rustc_parse::validate_attr; |
3dfed10e XL |
18 | use rustc_session::parse::feature_err; |
19 | use rustc_session::Session; | |
dfeec247 XL |
20 | use rustc_span::edition::{Edition, ALL_EDITIONS}; |
21 | use rustc_span::symbol::{sym, Symbol}; | |
22 | use rustc_span::{Span, DUMMY_SP}; | |
60c5eb7d | 23 | |
3157f602 XL |
24 | /// A folder that strips out items that do not belong in the current configuration. |
25 | pub struct StripUnconfigured<'a> { | |
3dfed10e | 26 | pub sess: &'a Session, |
3157f602 | 27 | pub features: Option<&'a Features>, |
cdc7bbd5 XL |
28 | /// If `true`, perform cfg-stripping on attached tokens. |
29 | /// This is only used for the input to derive macros, | |
30 | /// which needs eager expansion of `cfg` and `cfg_attr` | |
31 | pub config_tokens: bool, | |
1a4d82fc JJ |
32 | } |
33 | ||
dfeec247 | 34 | fn get_features( |
3dfed10e | 35 | sess: &Session, |
dfeec247 XL |
36 | span_handler: &Handler, |
37 | krate_attrs: &[ast::Attribute], | |
dfeec247 XL |
38 | ) -> Features { |
39 | fn feature_removed(span_handler: &Handler, span: Span, reason: Option<&str>) { | |
40 | let mut err = struct_span_err!(span_handler, span, E0557, "feature has been removed"); | |
41 | err.span_label(span, "feature has been removed"); | |
42 | if let Some(reason) = reason { | |
43 | err.note(reason); | |
44 | } | |
45 | err.emit(); | |
46 | } | |
47 | ||
48 | fn active_features_up_to(edition: Edition) -> impl Iterator<Item = &'static Feature> { | |
49 | ACTIVE_FEATURES.iter().filter(move |feature| { | |
50 | if let Some(feature_edition) = feature.edition { | |
51 | feature_edition <= edition | |
52 | } else { | |
53 | false | |
54 | } | |
55 | }) | |
56 | } | |
57 | ||
58 | let mut features = Features::default(); | |
59 | let mut edition_enabled_features = FxHashMap::default(); | |
3dfed10e | 60 | let crate_edition = sess.edition(); |
dfeec247 XL |
61 | |
62 | for &edition in ALL_EDITIONS { | |
63 | if edition <= crate_edition { | |
64 | // The `crate_edition` implies its respective umbrella feature-gate | |
65 | // (i.e., `#![feature(rust_20XX_preview)]` isn't needed on edition 20XX). | |
66 | edition_enabled_features.insert(edition.feature_name(), edition); | |
67 | } | |
68 | } | |
69 | ||
70 | for feature in active_features_up_to(crate_edition) { | |
71 | feature.set(&mut features, DUMMY_SP); | |
72 | edition_enabled_features.insert(feature.name, crate_edition); | |
73 | } | |
74 | ||
75 | // Process the edition umbrella feature-gates first, to ensure | |
76 | // `edition_enabled_features` is completed before it's queried. | |
77 | for attr in krate_attrs { | |
94222f64 | 78 | if !attr.has_name(sym::feature) { |
dfeec247 XL |
79 | continue; |
80 | } | |
81 | ||
82 | let list = match attr.meta_item_list() { | |
83 | Some(list) => list, | |
84 | None => continue, | |
85 | }; | |
86 | ||
87 | for mi in list { | |
88 | if !mi.is_word() { | |
89 | continue; | |
90 | } | |
91 | ||
92 | let name = mi.name_or_empty(); | |
93 | ||
94 | let edition = ALL_EDITIONS.iter().find(|e| name == e.feature_name()).copied(); | |
95 | if let Some(edition) = edition { | |
96 | if edition <= crate_edition { | |
97 | continue; | |
98 | } | |
99 | ||
100 | for feature in active_features_up_to(edition) { | |
101 | // FIXME(Manishearth) there is currently no way to set | |
102 | // lib features by edition | |
103 | feature.set(&mut features, DUMMY_SP); | |
104 | edition_enabled_features.insert(feature.name, edition); | |
105 | } | |
106 | } | |
107 | } | |
108 | } | |
109 | ||
110 | for attr in krate_attrs { | |
94222f64 | 111 | if !attr.has_name(sym::feature) { |
dfeec247 XL |
112 | continue; |
113 | } | |
114 | ||
115 | let list = match attr.meta_item_list() { | |
116 | Some(list) => list, | |
117 | None => continue, | |
118 | }; | |
119 | ||
120 | let bad_input = |span| { | |
121 | struct_span_err!(span_handler, span, E0556, "malformed `feature` attribute input") | |
122 | }; | |
123 | ||
124 | for mi in list { | |
125 | let name = match mi.ident() { | |
126 | Some(ident) if mi.is_word() => ident.name, | |
127 | Some(ident) => { | |
128 | bad_input(mi.span()) | |
129 | .span_suggestion( | |
130 | mi.span(), | |
131 | "expected just one word", | |
132 | format!("{}", ident.name), | |
133 | Applicability::MaybeIncorrect, | |
134 | ) | |
135 | .emit(); | |
136 | continue; | |
137 | } | |
138 | None => { | |
139 | bad_input(mi.span()).span_label(mi.span(), "expected just one word").emit(); | |
140 | continue; | |
141 | } | |
142 | }; | |
143 | ||
144 | if let Some(edition) = edition_enabled_features.get(&name) { | |
145 | let msg = | |
146 | &format!("the feature `{}` is included in the Rust {} edition", name, edition); | |
147 | span_handler.struct_span_warn_with_code(mi.span(), msg, error_code!(E0705)).emit(); | |
148 | continue; | |
149 | } | |
150 | ||
151 | if ALL_EDITIONS.iter().any(|e| name == e.feature_name()) { | |
152 | // Handled in the separate loop above. | |
153 | continue; | |
154 | } | |
155 | ||
156 | let removed = REMOVED_FEATURES.iter().find(|f| name == f.name); | |
157 | let stable_removed = STABLE_REMOVED_FEATURES.iter().find(|f| name == f.name); | |
158 | if let Some(Feature { state, .. }) = removed.or(stable_removed) { | |
159 | if let FeatureState::Removed { reason } | FeatureState::Stabilized { reason } = | |
160 | state | |
161 | { | |
162 | feature_removed(span_handler, mi.span(), *reason); | |
163 | continue; | |
164 | } | |
165 | } | |
166 | ||
167 | if let Some(Feature { since, .. }) = ACCEPTED_FEATURES.iter().find(|f| name == f.name) { | |
168 | let since = Some(Symbol::intern(since)); | |
169 | features.declared_lang_features.push((name, mi.span(), since)); | |
170 | continue; | |
171 | } | |
172 | ||
3dfed10e | 173 | if let Some(allowed) = sess.opts.debugging_opts.allow_features.as_ref() { |
a2a8927a | 174 | if allowed.iter().all(|f| name.as_str() != f) { |
dfeec247 XL |
175 | struct_span_err!( |
176 | span_handler, | |
177 | mi.span(), | |
178 | E0725, | |
179 | "the feature `{}` is not in the list of allowed features", | |
180 | name | |
181 | ) | |
182 | .emit(); | |
183 | continue; | |
184 | } | |
185 | } | |
186 | ||
187 | if let Some(f) = ACTIVE_FEATURES.iter().find(|f| name == f.name) { | |
188 | f.set(&mut features, mi.span()); | |
189 | features.declared_lang_features.push((name, mi.span(), None)); | |
190 | continue; | |
191 | } | |
192 | ||
193 | features.declared_lib_features.push((name, mi.span())); | |
194 | } | |
195 | } | |
196 | ||
197 | features | |
198 | } | |
199 | ||
9e0c209e | 200 | // `cfg_attr`-process the crate's attributes and compute the crate's features. |
3dfed10e | 201 | pub fn features(sess: &Session, mut krate: ast::Crate) -> (ast::Crate, Features) { |
cdc7bbd5 | 202 | let mut strip_unconfigured = StripUnconfigured { sess, features: None, config_tokens: false }; |
9e0c209e | 203 | |
74b04a01 | 204 | let unconfigured_attrs = krate.attrs.clone(); |
3dfed10e | 205 | let diag = &sess.parse_sess.span_diagnostic; |
74b04a01 | 206 | let err_count = diag.err_count(); |
6a06907d | 207 | let features = match strip_unconfigured.configure_krate_attrs(krate.attrs) { |
74b04a01 XL |
208 | None => { |
209 | // The entire crate is unconfigured. | |
9e0c209e | 210 | krate.attrs = Vec::new(); |
6a06907d | 211 | krate.items = Vec::new(); |
74b04a01 | 212 | Features::default() |
9e0c209e | 213 | } |
74b04a01 XL |
214 | Some(attrs) => { |
215 | krate.attrs = attrs; | |
3dfed10e | 216 | let features = get_features(sess, diag, &krate.attrs); |
74b04a01 XL |
217 | if err_count == diag.err_count() { |
218 | // Avoid reconfiguring malformed `cfg_attr`s. | |
219 | strip_unconfigured.features = Some(&features); | |
6a06907d XL |
220 | // Run configuration again, this time with features available |
221 | // so that we can perform feature-gating. | |
222 | strip_unconfigured.configure_krate_attrs(unconfigured_attrs); | |
74b04a01 XL |
223 | } |
224 | features | |
9e0c209e | 225 | } |
74b04a01 | 226 | }; |
9e0c209e SL |
227 | (krate, features) |
228 | } | |
229 | ||
e74abb32 | 230 | #[macro_export] |
9e0c209e SL |
231 | macro_rules! configure { |
232 | ($this:ident, $node:ident) => { | |
233 | match $this.configure($node) { | |
234 | Some(node) => node, | |
235 | None => return Default::default(), | |
236 | } | |
dfeec247 | 237 | }; |
9e0c209e SL |
238 | } |
239 | ||
3157f602 | 240 | impl<'a> StripUnconfigured<'a> { |
5099ac24 | 241 | pub fn configure<T: AstLike>(&self, mut node: T) -> Option<T> { |
9fa01778 | 242 | self.process_cfg_attrs(&mut node); |
5869c6ff | 243 | if self.in_cfg(node.attrs()) { |
cdc7bbd5 | 244 | self.try_configure_tokens(&mut node); |
5869c6ff XL |
245 | Some(node) |
246 | } else { | |
5869c6ff XL |
247 | None |
248 | } | |
1a4d82fc | 249 | } |
1a4d82fc | 250 | |
5099ac24 | 251 | fn try_configure_tokens<T: AstLike>(&self, node: &mut T) { |
cdc7bbd5 XL |
252 | if self.config_tokens { |
253 | if let Some(Some(tokens)) = node.tokens_mut() { | |
254 | let attr_annotated_tokens = tokens.create_token_stream(); | |
255 | *tokens = LazyTokenStream::new(self.configure_tokens(&attr_annotated_tokens)); | |
256 | } | |
257 | } | |
258 | } | |
259 | ||
5099ac24 | 260 | fn configure_krate_attrs(&self, mut attrs: Vec<ast::Attribute>) -> Option<Vec<ast::Attribute>> { |
6a06907d | 261 | attrs.flat_map_in_place(|attr| self.process_cfg_attr(attr)); |
cdc7bbd5 XL |
262 | if self.in_cfg(&attrs) { Some(attrs) } else { None } |
263 | } | |
264 | ||
265 | /// Performs cfg-expansion on `stream`, producing a new `AttrAnnotatedTokenStream`. | |
266 | /// This is only used during the invocation of `derive` proc-macros, | |
267 | /// which require that we cfg-expand their entire input. | |
268 | /// Normal cfg-expansion operates on parsed AST nodes via the `configure` method | |
5099ac24 | 269 | fn configure_tokens(&self, stream: &AttrAnnotatedTokenStream) -> AttrAnnotatedTokenStream { |
cdc7bbd5 XL |
270 | fn can_skip(stream: &AttrAnnotatedTokenStream) -> bool { |
271 | stream.0.iter().all(|(tree, _spacing)| match tree { | |
272 | AttrAnnotatedTokenTree::Attributes(_) => false, | |
273 | AttrAnnotatedTokenTree::Token(_) => true, | |
274 | AttrAnnotatedTokenTree::Delimited(_, _, inner) => can_skip(inner), | |
275 | }) | |
276 | } | |
277 | ||
278 | if can_skip(stream) { | |
279 | return stream.clone(); | |
6a06907d | 280 | } |
cdc7bbd5 XL |
281 | |
282 | let trees: Vec<_> = stream | |
283 | .0 | |
284 | .iter() | |
285 | .flat_map(|(tree, spacing)| match tree.clone() { | |
286 | AttrAnnotatedTokenTree::Attributes(mut data) => { | |
287 | let mut attrs: Vec<_> = std::mem::take(&mut data.attrs).into(); | |
288 | attrs.flat_map_in_place(|attr| self.process_cfg_attr(attr)); | |
289 | data.attrs = attrs.into(); | |
290 | ||
291 | if self.in_cfg(&data.attrs) { | |
292 | data.tokens = LazyTokenStream::new( | |
293 | self.configure_tokens(&data.tokens.create_token_stream()), | |
294 | ); | |
295 | Some((AttrAnnotatedTokenTree::Attributes(data), *spacing)).into_iter() | |
296 | } else { | |
297 | None.into_iter() | |
298 | } | |
299 | } | |
300 | AttrAnnotatedTokenTree::Delimited(sp, delim, mut inner) => { | |
301 | inner = self.configure_tokens(&inner); | |
302 | Some((AttrAnnotatedTokenTree::Delimited(sp, delim, inner), *spacing)) | |
303 | .into_iter() | |
304 | } | |
94222f64 XL |
305 | AttrAnnotatedTokenTree::Token(ref token) if let TokenKind::Interpolated(ref nt) = token.kind => { |
306 | panic!( | |
307 | "Nonterminal should have been flattened at {:?}: {:?}", | |
308 | token.span, nt | |
309 | ); | |
310 | } | |
cdc7bbd5 | 311 | AttrAnnotatedTokenTree::Token(token) => { |
94222f64 | 312 | Some((AttrAnnotatedTokenTree::Token(token), *spacing)).into_iter() |
cdc7bbd5 XL |
313 | } |
314 | }) | |
315 | .collect(); | |
316 | AttrAnnotatedTokenStream::new(trees) | |
6a06907d XL |
317 | } |
318 | ||
0bf4aa26 XL |
319 | /// Parse and expand all `cfg_attr` attributes into a list of attributes |
320 | /// that are within each `cfg_attr` that has a true configuration predicate. | |
321 | /// | |
74b04a01 | 322 | /// Gives compiler warnings if any `cfg_attr` does not contain any |
0bf4aa26 XL |
323 | /// attributes and is in the original source code. Gives compiler errors if |
324 | /// the syntax of any `cfg_attr` is incorrect. | |
5099ac24 | 325 | fn process_cfg_attrs<T: AstLike>(&self, node: &mut T) { |
9fa01778 XL |
326 | node.visit_attrs(|attrs| { |
327 | attrs.flat_map_in_place(|attr| self.process_cfg_attr(attr)); | |
328 | }); | |
1a4d82fc | 329 | } |
85aaf69f | 330 | |
5099ac24 FG |
331 | fn process_cfg_attr(&self, attr: Attribute) -> Vec<Attribute> { |
332 | if attr.has_name(sym::cfg_attr) { self.expand_cfg_attr(attr, true) } else { vec![attr] } | |
333 | } | |
334 | ||
0bf4aa26 XL |
335 | /// Parse and expand a single `cfg_attr` attribute into a list of attributes |
336 | /// when the configuration predicate is true, or otherwise expand into an | |
337 | /// empty list of attributes. | |
338 | /// | |
339 | /// Gives a compiler warning when the `cfg_attr` contains no attributes and | |
340 | /// is in the original source file. Gives a compiler error if the syntax of | |
9fa01778 | 341 | /// the attribute is incorrect. |
5099ac24 | 342 | crate fn expand_cfg_attr(&self, attr: Attribute, recursive: bool) -> Vec<Attribute> { |
94222f64 XL |
343 | let (cfg_predicate, expanded_attrs) = |
344 | match rustc_parse::parse_cfg_attr(&attr, &self.sess.parse_sess) { | |
345 | None => return vec![], | |
346 | Some(r) => r, | |
347 | }; | |
9e0c209e | 348 | |
dc9dc135 XL |
349 | // Lint on zero attributes in source. |
350 | if expanded_attrs.is_empty() { | |
5099ac24 FG |
351 | self.sess.parse_sess.buffer_lint( |
352 | rustc_lint_defs::builtin::UNUSED_ATTRIBUTES, | |
353 | attr.span, | |
354 | ast::CRATE_NODE_ID, | |
355 | "`#[cfg_attr]` does not expand to any attributes", | |
356 | ); | |
0bf4aa26 XL |
357 | } |
358 | ||
3dfed10e | 359 | if !attr::cfg_matches(&cfg_predicate, &self.sess.parse_sess, self.features) { |
60c5eb7d XL |
360 | return vec![]; |
361 | } | |
362 | ||
5099ac24 FG |
363 | if recursive { |
364 | // We call `process_cfg_attr` recursively in case there's a | |
365 | // `cfg_attr` inside of another `cfg_attr`. E.g. | |
366 | // `#[cfg_attr(false, cfg_attr(true, some_attr))]`. | |
367 | expanded_attrs | |
368 | .into_iter() | |
369 | .flat_map(|item| self.process_cfg_attr(self.expand_cfg_attr_item(&attr, item))) | |
370 | .collect() | |
371 | } else { | |
372 | expanded_attrs.into_iter().map(|item| self.expand_cfg_attr_item(&attr, item)).collect() | |
373 | } | |
374 | } | |
375 | ||
376 | fn expand_cfg_attr_item( | |
377 | &self, | |
378 | attr: &Attribute, | |
379 | (item, item_span): (ast::AttrItem, Span), | |
380 | ) -> Attribute { | |
381 | let orig_tokens = attr.tokens().to_tokenstream(); | |
382 | ||
383 | // We are taking an attribute of the form `#[cfg_attr(pred, attr)]` | |
384 | // and producing an attribute of the form `#[attr]`. We | |
385 | // have captured tokens for `attr` itself, but we need to | |
386 | // synthesize tokens for the wrapper `#` and `[]`, which | |
387 | // we do below. | |
388 | ||
389 | // Use the `#` in `#[cfg_attr(pred, attr)]` as the `#` token | |
390 | // for `attr` when we expand it to `#[attr]` | |
391 | let mut orig_trees = orig_tokens.trees(); | |
392 | let pound_token = match orig_trees.next().unwrap() { | |
393 | TokenTree::Token(token @ Token { kind: TokenKind::Pound, .. }) => token, | |
394 | _ => panic!("Bad tokens for attribute {:?}", attr), | |
395 | }; | |
396 | let pound_span = pound_token.span; | |
397 | ||
398 | let mut trees = vec![(AttrAnnotatedTokenTree::Token(pound_token), Spacing::Alone)]; | |
399 | if attr.style == AttrStyle::Inner { | |
400 | // For inner attributes, we do the same thing for the `!` in `#![some_attr]` | |
401 | let bang_token = match orig_trees.next().unwrap() { | |
402 | TokenTree::Token(token @ Token { kind: TokenKind::Not, .. }) => token, | |
403 | _ => panic!("Bad tokens for attribute {:?}", attr), | |
404 | }; | |
405 | trees.push((AttrAnnotatedTokenTree::Token(bang_token), Spacing::Alone)); | |
406 | } | |
407 | // We don't really have a good span to use for the syntheized `[]` | |
408 | // in `#[attr]`, so just use the span of the `#` token. | |
409 | let bracket_group = AttrAnnotatedTokenTree::Delimited( | |
410 | DelimSpan::from_single(pound_span), | |
411 | DelimToken::Bracket, | |
412 | item.tokens | |
413 | .as_ref() | |
414 | .unwrap_or_else(|| panic!("Missing tokens for {:?}", item)) | |
415 | .create_token_stream(), | |
416 | ); | |
417 | trees.push((bracket_group, Spacing::Alone)); | |
418 | let tokens = Some(LazyTokenStream::new(AttrAnnotatedTokenStream::new(trees))); | |
419 | let attr = attr::mk_attr_from_item(item, tokens, attr.style, item_span); | |
420 | if attr.has_name(sym::crate_type) { | |
421 | self.sess.parse_sess.buffer_lint( | |
422 | rustc_lint_defs::builtin::DEPRECATED_CFG_ATTR_CRATE_TYPE_NAME, | |
423 | attr.span, | |
424 | ast::CRATE_NODE_ID, | |
425 | "`crate_type` within an `#![cfg_attr] attribute is deprecated`", | |
426 | ); | |
427 | } | |
428 | if attr.has_name(sym::crate_name) { | |
429 | self.sess.parse_sess.buffer_lint( | |
430 | rustc_lint_defs::builtin::DEPRECATED_CFG_ATTR_CRATE_TYPE_NAME, | |
431 | attr.span, | |
432 | ast::CRATE_NODE_ID, | |
433 | "`crate_name` within an `#![cfg_attr] attribute is deprecated`", | |
434 | ); | |
435 | } | |
436 | attr | |
60c5eb7d XL |
437 | } |
438 | ||
9fa01778 | 439 | /// Determines if a node with the given attributes should be included in this configuration. |
6a06907d | 440 | fn in_cfg(&self, attrs: &[Attribute]) -> bool { |
5099ac24 FG |
441 | attrs.iter().all(|attr| !is_cfg(attr) || self.cfg_true(attr)) |
442 | } | |
443 | ||
444 | crate fn cfg_true(&self, attr: &Attribute) -> bool { | |
445 | let meta_item = match validate_attr::parse_meta(&self.sess.parse_sess, attr) { | |
446 | Ok(meta_item) => meta_item, | |
447 | Err(mut err) => { | |
448 | err.emit(); | |
cc61c64b | 449 | return true; |
8faf50e0 | 450 | } |
5099ac24 FG |
451 | }; |
452 | parse_cfg(&meta_item, &self.sess).map_or(true, |meta_item| { | |
453 | attr::cfg_matches(&meta_item, &self.sess.parse_sess, self.features) | |
3157f602 XL |
454 | }) |
455 | } | |
92a42be0 | 456 | |
0531ce1d | 457 | /// If attributes are not allowed on expressions, emit an error for `attr` |
6a06907d | 458 | crate fn maybe_emit_expr_attr_err(&self, attr: &Attribute) { |
5869c6ff | 459 | if !self.features.map_or(true, |features| features.stmt_expr_attributes) { |
dfeec247 | 460 | let mut err = feature_err( |
3dfed10e | 461 | &self.sess.parse_sess, |
dfeec247 XL |
462 | sym::stmt_expr_attributes, |
463 | attr.span, | |
464 | "attributes on expressions are experimental", | |
465 | ); | |
0531ce1d | 466 | |
60c5eb7d | 467 | if attr.is_doc_comment() { |
0531ce1d | 468 | err.help("`///` is for documentation comments. For a plain comment, use `//`."); |
92a42be0 | 469 | } |
0531ce1d XL |
470 | |
471 | err.emit(); | |
92a42be0 SL |
472 | } |
473 | } | |
92a42be0 | 474 | |
5099ac24 | 475 | pub fn configure_expr(&self, expr: &mut P<ast::Expr>) { |
6a06907d XL |
476 | for attr in expr.attrs.iter() { |
477 | self.maybe_emit_expr_attr_err(attr); | |
478 | } | |
3157f602 XL |
479 | |
480 | // If an expr is valid to cfg away it will have been removed by the | |
481 | // outer stmt or expression folder before descending in here. | |
482 | // Anything else is always required, and thus has to error out | |
483 | // in case of a cfg attr. | |
484 | // | |
9fa01778 XL |
485 | // N.B., this is intentionally not part of the visit_expr() function |
486 | // in order for filter_map_expr() to be able to avoid this check | |
94222f64 | 487 | if let Some(attr) = expr.attrs().iter().find(|a| is_cfg(*a)) { |
3157f602 | 488 | let msg = "removing an expression is not supported in this position"; |
3dfed10e | 489 | self.sess.parse_sess.span_diagnostic.span_err(attr.span, msg); |
92a42be0 | 490 | } |
3157f602 | 491 | |
cdc7bbd5 XL |
492 | self.process_cfg_attrs(expr); |
493 | self.try_configure_tokens(&mut *expr); | |
92a42be0 | 494 | } |
92a42be0 SL |
495 | } |
496 | ||
17df50a5 XL |
497 | pub fn parse_cfg<'a>(meta_item: &'a MetaItem, sess: &Session) -> Option<&'a MetaItem> { |
498 | let error = |span, msg, suggestion: &str| { | |
499 | let mut err = sess.parse_sess.span_diagnostic.struct_span_err(span, msg); | |
500 | if !suggestion.is_empty() { | |
501 | err.span_suggestion( | |
502 | span, | |
503 | "expected syntax is", | |
504 | suggestion.into(), | |
505 | Applicability::HasPlaceholders, | |
506 | ); | |
507 | } | |
508 | err.emit(); | |
509 | None | |
510 | }; | |
511 | let span = meta_item.span; | |
512 | match meta_item.meta_item_list() { | |
513 | None => error(span, "`cfg` is not followed by parentheses", "cfg(/* predicate */)"), | |
514 | Some([]) => error(span, "`cfg` predicate is not specified", ""), | |
515 | Some([_, .., l]) => error(l.span(), "multiple `cfg` predicates are specified", ""), | |
516 | Some([single]) => match single.meta_item() { | |
517 | Some(meta_item) => Some(meta_item), | |
518 | None => error(single.span(), "`cfg` predicate key cannot be a literal", ""), | |
519 | }, | |
520 | } | |
521 | } | |
522 | ||
94222f64 XL |
523 | fn is_cfg(attr: &Attribute) -> bool { |
524 | attr.has_name(sym::cfg) | |
92a42be0 | 525 | } |