]>
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}; | |
8 | use rustc_ast::{self as ast, AstLike, AttrItem, 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 | }; | |
ba9703b0 | 17 | use rustc_parse::{parse_in, 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 { | |
3dfed10e | 78 | if !sess.check_name(attr, 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 { | |
3dfed10e | 111 | if !sess.check_name(attr, 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() { |
dfeec247 XL |
174 | if allowed.iter().find(|&f| name.as_str() == *f).is_none() { |
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 | ||
60c5eb7d XL |
240 | const CFG_ATTR_GRAMMAR_HELP: &str = "#[cfg_attr(condition, attribute, other_attribute, ...)]"; |
241 | const CFG_ATTR_NOTE_REF: &str = "for more information, visit \ | |
242 | <https://doc.rust-lang.org/reference/conditional-compilation.html\ | |
243 | #the-cfg_attr-attribute>"; | |
244 | ||
3157f602 | 245 | impl<'a> StripUnconfigured<'a> { |
6a06907d | 246 | pub fn configure<T: AstLike>(&mut self, mut node: T) -> Option<T> { |
9fa01778 | 247 | self.process_cfg_attrs(&mut node); |
5869c6ff | 248 | if self.in_cfg(node.attrs()) { |
cdc7bbd5 | 249 | self.try_configure_tokens(&mut node); |
5869c6ff XL |
250 | Some(node) |
251 | } else { | |
5869c6ff XL |
252 | None |
253 | } | |
1a4d82fc | 254 | } |
1a4d82fc | 255 | |
cdc7bbd5 XL |
256 | fn try_configure_tokens<T: AstLike>(&mut self, node: &mut T) { |
257 | if self.config_tokens { | |
258 | if let Some(Some(tokens)) = node.tokens_mut() { | |
259 | let attr_annotated_tokens = tokens.create_token_stream(); | |
260 | *tokens = LazyTokenStream::new(self.configure_tokens(&attr_annotated_tokens)); | |
261 | } | |
262 | } | |
263 | } | |
264 | ||
6a06907d XL |
265 | fn configure_krate_attrs( |
266 | &mut self, | |
267 | mut attrs: Vec<ast::Attribute>, | |
268 | ) -> Option<Vec<ast::Attribute>> { | |
269 | attrs.flat_map_in_place(|attr| self.process_cfg_attr(attr)); | |
cdc7bbd5 XL |
270 | if self.in_cfg(&attrs) { Some(attrs) } else { None } |
271 | } | |
272 | ||
273 | /// Performs cfg-expansion on `stream`, producing a new `AttrAnnotatedTokenStream`. | |
274 | /// This is only used during the invocation of `derive` proc-macros, | |
275 | /// which require that we cfg-expand their entire input. | |
276 | /// Normal cfg-expansion operates on parsed AST nodes via the `configure` method | |
277 | fn configure_tokens(&mut self, stream: &AttrAnnotatedTokenStream) -> AttrAnnotatedTokenStream { | |
278 | fn can_skip(stream: &AttrAnnotatedTokenStream) -> bool { | |
279 | stream.0.iter().all(|(tree, _spacing)| match tree { | |
280 | AttrAnnotatedTokenTree::Attributes(_) => false, | |
281 | AttrAnnotatedTokenTree::Token(_) => true, | |
282 | AttrAnnotatedTokenTree::Delimited(_, _, inner) => can_skip(inner), | |
283 | }) | |
284 | } | |
285 | ||
286 | if can_skip(stream) { | |
287 | return stream.clone(); | |
6a06907d | 288 | } |
cdc7bbd5 XL |
289 | |
290 | let trees: Vec<_> = stream | |
291 | .0 | |
292 | .iter() | |
293 | .flat_map(|(tree, spacing)| match tree.clone() { | |
294 | AttrAnnotatedTokenTree::Attributes(mut data) => { | |
295 | let mut attrs: Vec<_> = std::mem::take(&mut data.attrs).into(); | |
296 | attrs.flat_map_in_place(|attr| self.process_cfg_attr(attr)); | |
297 | data.attrs = attrs.into(); | |
298 | ||
299 | if self.in_cfg(&data.attrs) { | |
300 | data.tokens = LazyTokenStream::new( | |
301 | self.configure_tokens(&data.tokens.create_token_stream()), | |
302 | ); | |
303 | Some((AttrAnnotatedTokenTree::Attributes(data), *spacing)).into_iter() | |
304 | } else { | |
305 | None.into_iter() | |
306 | } | |
307 | } | |
308 | AttrAnnotatedTokenTree::Delimited(sp, delim, mut inner) => { | |
309 | inner = self.configure_tokens(&inner); | |
310 | Some((AttrAnnotatedTokenTree::Delimited(sp, delim, inner), *spacing)) | |
311 | .into_iter() | |
312 | } | |
313 | AttrAnnotatedTokenTree::Token(token) => { | |
314 | if let TokenKind::Interpolated(nt) = token.kind { | |
315 | panic!( | |
316 | "Nonterminal should have been flattened at {:?}: {:?}", | |
317 | token.span, nt | |
318 | ); | |
319 | } else { | |
320 | Some((AttrAnnotatedTokenTree::Token(token), *spacing)).into_iter() | |
321 | } | |
322 | } | |
323 | }) | |
324 | .collect(); | |
325 | AttrAnnotatedTokenStream::new(trees) | |
6a06907d XL |
326 | } |
327 | ||
0bf4aa26 XL |
328 | /// Parse and expand all `cfg_attr` attributes into a list of attributes |
329 | /// that are within each `cfg_attr` that has a true configuration predicate. | |
330 | /// | |
74b04a01 | 331 | /// Gives compiler warnings if any `cfg_attr` does not contain any |
0bf4aa26 XL |
332 | /// attributes and is in the original source code. Gives compiler errors if |
333 | /// the syntax of any `cfg_attr` is incorrect. | |
6a06907d | 334 | fn process_cfg_attrs<T: AstLike>(&mut self, node: &mut T) { |
9fa01778 XL |
335 | node.visit_attrs(|attrs| { |
336 | attrs.flat_map_in_place(|attr| self.process_cfg_attr(attr)); | |
337 | }); | |
1a4d82fc | 338 | } |
85aaf69f | 339 | |
0bf4aa26 XL |
340 | /// Parse and expand a single `cfg_attr` attribute into a list of attributes |
341 | /// when the configuration predicate is true, or otherwise expand into an | |
342 | /// empty list of attributes. | |
343 | /// | |
344 | /// Gives a compiler warning when the `cfg_attr` contains no attributes and | |
345 | /// is in the original source file. Gives a compiler error if the syntax of | |
9fa01778 | 346 | /// the attribute is incorrect. |
60c5eb7d XL |
347 | fn process_cfg_attr(&mut self, attr: Attribute) -> Vec<Attribute> { |
348 | if !attr.has_name(sym::cfg_attr) { | |
0bf4aa26 | 349 | return vec![attr]; |
85aaf69f SL |
350 | } |
351 | ||
60c5eb7d XL |
352 | let (cfg_predicate, expanded_attrs) = match self.parse_cfg_attr(&attr) { |
353 | None => return vec![], | |
354 | Some(r) => r, | |
d9579d0f | 355 | }; |
9e0c209e | 356 | |
dc9dc135 XL |
357 | // Lint on zero attributes in source. |
358 | if expanded_attrs.is_empty() { | |
359 | return vec![attr]; | |
0bf4aa26 XL |
360 | } |
361 | ||
dc9dc135 | 362 | // At this point we know the attribute is considered used. |
3dfed10e | 363 | self.sess.mark_attr_used(&attr); |
dc9dc135 | 364 | |
3dfed10e | 365 | if !attr::cfg_matches(&cfg_predicate, &self.sess.parse_sess, self.features) { |
60c5eb7d XL |
366 | return vec![]; |
367 | } | |
368 | ||
369 | // We call `process_cfg_attr` recursively in case there's a | |
370 | // `cfg_attr` inside of another `cfg_attr`. E.g. | |
371 | // `#[cfg_attr(false, cfg_attr(true, some_attr))]`. | |
372 | expanded_attrs | |
373 | .into_iter() | |
374 | .flat_map(|(item, span)| { | |
cdc7bbd5 | 375 | let orig_tokens = attr.tokens().to_tokenstream(); |
29967ef6 XL |
376 | |
377 | // We are taking an attribute of the form `#[cfg_attr(pred, attr)]` | |
378 | // and producing an attribute of the form `#[attr]`. We | |
379 | // have captured tokens for `attr` itself, but we need to | |
380 | // synthesize tokens for the wrapper `#` and `[]`, which | |
381 | // we do below. | |
382 | ||
383 | // Use the `#` in `#[cfg_attr(pred, attr)]` as the `#` token | |
384 | // for `attr` when we expand it to `#[attr]` | |
cdc7bbd5 XL |
385 | let mut orig_trees = orig_tokens.trees(); |
386 | let pound_token = match orig_trees.next().unwrap() { | |
387 | TokenTree::Token(token @ Token { kind: TokenKind::Pound, .. }) => token, | |
388 | _ => panic!("Bad tokens for attribute {:?}", attr), | |
389 | }; | |
390 | let pound_span = pound_token.span; | |
391 | ||
392 | let mut trees = vec![(AttrAnnotatedTokenTree::Token(pound_token), Spacing::Alone)]; | |
393 | if attr.style == AttrStyle::Inner { | |
394 | // For inner attributes, we do the same thing for the `!` in `#![some_attr]` | |
395 | let bang_token = match orig_trees.next().unwrap() { | |
396 | TokenTree::Token(token @ Token { kind: TokenKind::Not, .. }) => token, | |
397 | _ => panic!("Bad tokens for attribute {:?}", attr), | |
398 | }; | |
399 | trees.push((AttrAnnotatedTokenTree::Token(bang_token), Spacing::Alone)); | |
29967ef6 XL |
400 | } |
401 | // We don't really have a good span to use for the syntheized `[]` | |
402 | // in `#[attr]`, so just use the span of the `#` token. | |
cdc7bbd5 XL |
403 | let bracket_group = AttrAnnotatedTokenTree::Delimited( |
404 | DelimSpan::from_single(pound_span), | |
29967ef6 XL |
405 | DelimToken::Bracket, |
406 | item.tokens | |
407 | .as_ref() | |
408 | .unwrap_or_else(|| panic!("Missing tokens for {:?}", item)) | |
409 | .create_token_stream(), | |
410 | ); | |
cdc7bbd5 XL |
411 | trees.push((bracket_group, Spacing::Alone)); |
412 | let tokens = Some(LazyTokenStream::new(AttrAnnotatedTokenStream::new(trees))); | |
29967ef6 | 413 | self.process_cfg_attr(attr::mk_attr_from_item(item, tokens, attr.style, span)) |
60c5eb7d | 414 | }) |
0bf4aa26 | 415 | .collect() |
60c5eb7d XL |
416 | } |
417 | ||
418 | fn parse_cfg_attr(&self, attr: &Attribute) -> Option<(MetaItem, Vec<(AttrItem, Span)>)> { | |
419 | match attr.get_normal_item().args { | |
420 | ast::MacArgs::Delimited(dspan, delim, ref tts) if !tts.is_empty() => { | |
421 | let msg = "wrong `cfg_attr` delimiters"; | |
3dfed10e XL |
422 | validate_attr::check_meta_bad_delim(&self.sess.parse_sess, dspan, delim, msg); |
423 | match parse_in(&self.sess.parse_sess, tts.clone(), "`cfg_attr` input", |p| { | |
424 | p.parse_cfg_attr() | |
425 | }) { | |
60c5eb7d | 426 | Ok(r) => return Some(r), |
74b04a01 XL |
427 | Err(mut e) => { |
428 | e.help(&format!("the valid syntax is `{}`", CFG_ATTR_GRAMMAR_HELP)) | |
429 | .note(CFG_ATTR_NOTE_REF) | |
430 | .emit(); | |
431 | } | |
60c5eb7d XL |
432 | } |
433 | } | |
434 | _ => self.error_malformed_cfg_attr_missing(attr.span), | |
85aaf69f | 435 | } |
60c5eb7d XL |
436 | None |
437 | } | |
438 | ||
439 | fn error_malformed_cfg_attr_missing(&self, span: Span) { | |
440 | self.sess | |
3dfed10e | 441 | .parse_sess |
60c5eb7d XL |
442 | .span_diagnostic |
443 | .struct_span_err(span, "malformed `cfg_attr` attribute input") | |
444 | .span_suggestion( | |
445 | span, | |
446 | "missing condition and attribute", | |
447 | CFG_ATTR_GRAMMAR_HELP.to_string(), | |
448 | Applicability::HasPlaceholders, | |
449 | ) | |
450 | .note(CFG_ATTR_NOTE_REF) | |
451 | .emit(); | |
85aaf69f SL |
452 | } |
453 | ||
9fa01778 | 454 | /// Determines if a node with the given attributes should be included in this configuration. |
6a06907d | 455 | fn in_cfg(&self, attrs: &[Attribute]) -> bool { |
3157f602 | 456 | attrs.iter().all(|attr| { |
3dfed10e | 457 | if !is_cfg(self.sess, attr) { |
cc61c64b | 458 | return true; |
8faf50e0 | 459 | } |
3dfed10e | 460 | let meta_item = match validate_attr::parse_meta(&self.sess.parse_sess, attr) { |
74b04a01 XL |
461 | Ok(meta_item) => meta_item, |
462 | Err(mut err) => { | |
463 | err.emit(); | |
464 | return true; | |
465 | } | |
466 | }; | |
8faf50e0 | 467 | let error = |span, msg, suggestion: &str| { |
3dfed10e | 468 | let mut err = self.sess.parse_sess.span_diagnostic.struct_span_err(span, msg); |
8faf50e0 | 469 | if !suggestion.is_empty() { |
9fa01778 | 470 | err.span_suggestion( |
0bf4aa26 XL |
471 | span, |
472 | "expected syntax is", | |
473 | suggestion.into(), | |
474 | Applicability::MaybeIncorrect, | |
475 | ); | |
8faf50e0 XL |
476 | } |
477 | err.emit(); | |
478 | true | |
479 | }; | |
74b04a01 XL |
480 | let span = meta_item.span; |
481 | match meta_item.meta_item_list() { | |
482 | None => error(span, "`cfg` is not followed by parentheses", "cfg(/* predicate */)"), | |
483 | Some([]) => error(span, "`cfg` predicate is not specified", ""), | |
484 | Some([_, .., l]) => error(l.span(), "multiple `cfg` predicates are specified", ""), | |
485 | Some([single]) => match single.meta_item() { | |
3dfed10e XL |
486 | Some(meta_item) => { |
487 | attr::cfg_matches(meta_item, &self.sess.parse_sess, self.features) | |
488 | } | |
74b04a01 XL |
489 | None => error(single.span(), "`cfg` predicate key cannot be a literal", ""), |
490 | }, | |
9e0c209e | 491 | } |
3157f602 XL |
492 | }) |
493 | } | |
92a42be0 | 494 | |
0531ce1d | 495 | /// If attributes are not allowed on expressions, emit an error for `attr` |
6a06907d | 496 | crate fn maybe_emit_expr_attr_err(&self, attr: &Attribute) { |
5869c6ff | 497 | if !self.features.map_or(true, |features| features.stmt_expr_attributes) { |
dfeec247 | 498 | let mut err = feature_err( |
3dfed10e | 499 | &self.sess.parse_sess, |
dfeec247 XL |
500 | sym::stmt_expr_attributes, |
501 | attr.span, | |
502 | "attributes on expressions are experimental", | |
503 | ); | |
0531ce1d | 504 | |
60c5eb7d | 505 | if attr.is_doc_comment() { |
0531ce1d | 506 | err.help("`///` is for documentation comments. For a plain comment, use `//`."); |
92a42be0 | 507 | } |
0531ce1d XL |
508 | |
509 | err.emit(); | |
92a42be0 SL |
510 | } |
511 | } | |
92a42be0 | 512 | |
9fa01778 | 513 | pub fn configure_expr(&mut self, expr: &mut P<ast::Expr>) { |
6a06907d XL |
514 | for attr in expr.attrs.iter() { |
515 | self.maybe_emit_expr_attr_err(attr); | |
516 | } | |
3157f602 XL |
517 | |
518 | // If an expr is valid to cfg away it will have been removed by the | |
519 | // outer stmt or expression folder before descending in here. | |
520 | // Anything else is always required, and thus has to error out | |
521 | // in case of a cfg attr. | |
522 | // | |
9fa01778 XL |
523 | // N.B., this is intentionally not part of the visit_expr() function |
524 | // in order for filter_map_expr() to be able to avoid this check | |
3dfed10e | 525 | if let Some(attr) = expr.attrs().iter().find(|a| is_cfg(self.sess, a)) { |
3157f602 | 526 | let msg = "removing an expression is not supported in this position"; |
3dfed10e | 527 | self.sess.parse_sess.span_diagnostic.span_err(attr.span, msg); |
92a42be0 | 528 | } |
3157f602 | 529 | |
cdc7bbd5 XL |
530 | self.process_cfg_attrs(expr); |
531 | self.try_configure_tokens(&mut *expr); | |
92a42be0 | 532 | } |
92a42be0 SL |
533 | } |
534 | ||
3dfed10e XL |
535 | fn is_cfg(sess: &Session, attr: &Attribute) -> bool { |
536 | sess.check_name(attr, sym::cfg) | |
92a42be0 | 537 | } |