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