]>
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; |
dfeec247 | 15 | use rustc_data_structures::fx::FxHashMap; |
ba9703b0 | 16 | use rustc_data_structures::map_in_place::MapInPlace; |
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 | ||
9c376795 FG |
39 | fn get_features(sess: &Session, krate_attrs: &[ast::Attribute]) -> Features { |
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 | ||
487cf647 | 193 | /// `cfg_attr`-process the crate's attributes and compute the crate's features. |
5e7ed085 FG |
194 | pub fn features( |
195 | sess: &Session, | |
196 | mut krate: ast::Crate, | |
197 | lint_node_id: NodeId, | |
198 | ) -> (ast::Crate, Features) { | |
199 | let mut strip_unconfigured = | |
200 | StripUnconfigured { sess, features: None, config_tokens: false, lint_node_id }; | |
9e0c209e | 201 | |
74b04a01 | 202 | let unconfigured_attrs = krate.attrs.clone(); |
3dfed10e | 203 | let diag = &sess.parse_sess.span_diagnostic; |
74b04a01 | 204 | let err_count = diag.err_count(); |
6a06907d | 205 | let features = match strip_unconfigured.configure_krate_attrs(krate.attrs) { |
74b04a01 XL |
206 | None => { |
207 | // The entire crate is unconfigured. | |
f2b60f7d | 208 | krate.attrs = ast::AttrVec::new(); |
6a06907d | 209 | krate.items = Vec::new(); |
74b04a01 | 210 | Features::default() |
9e0c209e | 211 | } |
74b04a01 XL |
212 | Some(attrs) => { |
213 | krate.attrs = attrs; | |
9c376795 | 214 | let features = get_features(sess, &krate.attrs); |
74b04a01 XL |
215 | if err_count == diag.err_count() { |
216 | // Avoid reconfiguring malformed `cfg_attr`s. | |
217 | strip_unconfigured.features = Some(&features); | |
6a06907d XL |
218 | // Run configuration again, this time with features available |
219 | // so that we can perform feature-gating. | |
220 | strip_unconfigured.configure_krate_attrs(unconfigured_attrs); | |
74b04a01 XL |
221 | } |
222 | features | |
9e0c209e | 223 | } |
74b04a01 | 224 | }; |
9e0c209e SL |
225 | (krate, features) |
226 | } | |
227 | ||
e74abb32 | 228 | #[macro_export] |
9e0c209e SL |
229 | macro_rules! configure { |
230 | ($this:ident, $node:ident) => { | |
231 | match $this.configure($node) { | |
232 | Some(node) => node, | |
233 | None => return Default::default(), | |
234 | } | |
dfeec247 | 235 | }; |
9e0c209e SL |
236 | } |
237 | ||
3157f602 | 238 | impl<'a> StripUnconfigured<'a> { |
04454e1e | 239 | pub fn configure<T: HasAttrs + HasTokens>(&self, mut node: T) -> Option<T> { |
9fa01778 | 240 | self.process_cfg_attrs(&mut node); |
5869c6ff | 241 | if self.in_cfg(node.attrs()) { |
cdc7bbd5 | 242 | self.try_configure_tokens(&mut node); |
5869c6ff XL |
243 | Some(node) |
244 | } else { | |
5869c6ff XL |
245 | None |
246 | } | |
1a4d82fc | 247 | } |
1a4d82fc | 248 | |
04454e1e | 249 | fn try_configure_tokens<T: HasTokens>(&self, node: &mut T) { |
cdc7bbd5 XL |
250 | if self.config_tokens { |
251 | if let Some(Some(tokens)) = node.tokens_mut() { | |
f2b60f7d FG |
252 | let attr_stream = tokens.to_attr_token_stream(); |
253 | *tokens = LazyAttrTokenStream::new(self.configure_tokens(&attr_stream)); | |
cdc7bbd5 XL |
254 | } |
255 | } | |
256 | } | |
257 | ||
f2b60f7d | 258 | fn configure_krate_attrs(&self, mut attrs: ast::AttrVec) -> Option<ast::AttrVec> { |
6a06907d | 259 | attrs.flat_map_in_place(|attr| self.process_cfg_attr(attr)); |
cdc7bbd5 XL |
260 | if self.in_cfg(&attrs) { Some(attrs) } else { None } |
261 | } | |
262 | ||
f2b60f7d | 263 | /// Performs cfg-expansion on `stream`, producing a new `AttrTokenStream`. |
cdc7bbd5 XL |
264 | /// This is only used during the invocation of `derive` proc-macros, |
265 | /// which require that we cfg-expand their entire input. | |
266 | /// Normal cfg-expansion operates on parsed AST nodes via the `configure` method | |
f2b60f7d FG |
267 | fn configure_tokens(&self, stream: &AttrTokenStream) -> AttrTokenStream { |
268 | fn can_skip(stream: &AttrTokenStream) -> bool { | |
269 | stream.0.iter().all(|tree| match tree { | |
270 | AttrTokenTree::Attributes(_) => false, | |
271 | AttrTokenTree::Token(..) => true, | |
272 | AttrTokenTree::Delimited(_, _, inner) => can_skip(inner), | |
cdc7bbd5 XL |
273 | }) |
274 | } | |
275 | ||
276 | if can_skip(stream) { | |
277 | return stream.clone(); | |
6a06907d | 278 | } |
cdc7bbd5 XL |
279 | |
280 | let trees: Vec<_> = stream | |
281 | .0 | |
282 | .iter() | |
f2b60f7d FG |
283 | .flat_map(|tree| match tree.clone() { |
284 | AttrTokenTree::Attributes(mut data) => { | |
285 | data.attrs.flat_map_in_place(|attr| self.process_cfg_attr(attr)); | |
cdc7bbd5 XL |
286 | |
287 | if self.in_cfg(&data.attrs) { | |
f2b60f7d FG |
288 | data.tokens = LazyAttrTokenStream::new( |
289 | self.configure_tokens(&data.tokens.to_attr_token_stream()), | |
cdc7bbd5 | 290 | ); |
f2b60f7d | 291 | Some(AttrTokenTree::Attributes(data)).into_iter() |
cdc7bbd5 XL |
292 | } else { |
293 | None.into_iter() | |
294 | } | |
295 | } | |
f2b60f7d | 296 | AttrTokenTree::Delimited(sp, delim, mut inner) => { |
cdc7bbd5 | 297 | inner = self.configure_tokens(&inner); |
f2b60f7d | 298 | Some(AttrTokenTree::Delimited(sp, delim, inner)) |
cdc7bbd5 XL |
299 | .into_iter() |
300 | } | |
9c376795 | 301 | AttrTokenTree::Token(ref token, _) if let TokenKind::Interpolated(nt) = &token.kind => { |
94222f64 XL |
302 | panic!( |
303 | "Nonterminal should have been flattened at {:?}: {:?}", | |
304 | token.span, nt | |
305 | ); | |
306 | } | |
f2b60f7d FG |
307 | AttrTokenTree::Token(token, spacing) => { |
308 | Some(AttrTokenTree::Token(token, spacing)).into_iter() | |
cdc7bbd5 XL |
309 | } |
310 | }) | |
311 | .collect(); | |
f2b60f7d | 312 | AttrTokenStream::new(trees) |
6a06907d XL |
313 | } |
314 | ||
0bf4aa26 XL |
315 | /// Parse and expand all `cfg_attr` attributes into a list of attributes |
316 | /// that are within each `cfg_attr` that has a true configuration predicate. | |
317 | /// | |
74b04a01 | 318 | /// Gives compiler warnings if any `cfg_attr` does not contain any |
0bf4aa26 XL |
319 | /// attributes and is in the original source code. Gives compiler errors if |
320 | /// the syntax of any `cfg_attr` is incorrect. | |
04454e1e | 321 | fn process_cfg_attrs<T: HasAttrs>(&self, node: &mut T) { |
9fa01778 XL |
322 | node.visit_attrs(|attrs| { |
323 | attrs.flat_map_in_place(|attr| self.process_cfg_attr(attr)); | |
324 | }); | |
1a4d82fc | 325 | } |
85aaf69f | 326 | |
5099ac24 FG |
327 | fn process_cfg_attr(&self, attr: Attribute) -> Vec<Attribute> { |
328 | if attr.has_name(sym::cfg_attr) { self.expand_cfg_attr(attr, true) } else { vec![attr] } | |
329 | } | |
330 | ||
0bf4aa26 XL |
331 | /// Parse and expand a single `cfg_attr` attribute into a list of attributes |
332 | /// when the configuration predicate is true, or otherwise expand into an | |
333 | /// empty list of attributes. | |
334 | /// | |
335 | /// Gives a compiler warning when the `cfg_attr` contains no attributes and | |
336 | /// is in the original source file. Gives a compiler error if the syntax of | |
9fa01778 | 337 | /// the attribute is incorrect. |
923072b8 | 338 | pub(crate) fn expand_cfg_attr(&self, attr: Attribute, recursive: bool) -> Vec<Attribute> { |
5e7ed085 FG |
339 | let Some((cfg_predicate, expanded_attrs)) = |
340 | rustc_parse::parse_cfg_attr(&attr, &self.sess.parse_sess) else { | |
341 | return vec![]; | |
94222f64 | 342 | }; |
9e0c209e | 343 | |
dc9dc135 XL |
344 | // Lint on zero attributes in source. |
345 | if expanded_attrs.is_empty() { | |
5099ac24 FG |
346 | self.sess.parse_sess.buffer_lint( |
347 | rustc_lint_defs::builtin::UNUSED_ATTRIBUTES, | |
348 | attr.span, | |
349 | ast::CRATE_NODE_ID, | |
350 | "`#[cfg_attr]` does not expand to any attributes", | |
351 | ); | |
0bf4aa26 XL |
352 | } |
353 | ||
5e7ed085 FG |
354 | if !attr::cfg_matches( |
355 | &cfg_predicate, | |
356 | &self.sess.parse_sess, | |
357 | self.lint_node_id, | |
358 | self.features, | |
359 | ) { | |
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 { | |
f2b60f7d | 381 | let orig_tokens = attr.tokens(); |
5099ac24 FG |
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]` | |
923072b8 | 391 | let mut orig_trees = orig_tokens.into_trees(); |
064997fb | 392 | let TokenTree::Token(pound_token @ Token { kind: TokenKind::Pound, .. }, _) = orig_trees.next().unwrap() else { |
5e7ed085 | 393 | panic!("Bad tokens for attribute {:?}", attr); |
5099ac24 FG |
394 | }; |
395 | let pound_span = pound_token.span; | |
396 | ||
f2b60f7d | 397 | let mut trees = vec![AttrTokenTree::Token(pound_token, Spacing::Alone)]; |
5099ac24 FG |
398 | if attr.style == AttrStyle::Inner { |
399 | // For inner attributes, we do the same thing for the `!` in `#![some_attr]` | |
064997fb | 400 | let TokenTree::Token(bang_token @ Token { kind: TokenKind::Not, .. }, _) = orig_trees.next().unwrap() else { |
5e7ed085 | 401 | panic!("Bad tokens for attribute {:?}", attr); |
5099ac24 | 402 | }; |
f2b60f7d | 403 | trees.push(AttrTokenTree::Token(bang_token, Spacing::Alone)); |
5099ac24 | 404 | } |
5e7ed085 | 405 | // We don't really have a good span to use for the synthesized `[]` |
5099ac24 | 406 | // in `#[attr]`, so just use the span of the `#` token. |
f2b60f7d | 407 | let bracket_group = AttrTokenTree::Delimited( |
5099ac24 | 408 | DelimSpan::from_single(pound_span), |
04454e1e | 409 | Delimiter::Bracket, |
5099ac24 FG |
410 | item.tokens |
411 | .as_ref() | |
412 | .unwrap_or_else(|| panic!("Missing tokens for {:?}", item)) | |
f2b60f7d FG |
413 | .to_attr_token_stream(), |
414 | ); | |
415 | trees.push(bracket_group); | |
416 | let tokens = Some(LazyAttrTokenStream::new(AttrTokenStream::new(trees))); | |
417 | let attr = attr::mk_attr_from_item( | |
418 | &self.sess.parse_sess.attr_id_generator, | |
419 | item, | |
420 | tokens, | |
421 | attr.style, | |
422 | item_span, | |
5099ac24 | 423 | ); |
5099ac24 FG |
424 | if attr.has_name(sym::crate_type) { |
425 | self.sess.parse_sess.buffer_lint( | |
426 | rustc_lint_defs::builtin::DEPRECATED_CFG_ATTR_CRATE_TYPE_NAME, | |
427 | attr.span, | |
428 | ast::CRATE_NODE_ID, | |
429 | "`crate_type` within an `#![cfg_attr] attribute is deprecated`", | |
430 | ); | |
431 | } | |
432 | if attr.has_name(sym::crate_name) { | |
433 | self.sess.parse_sess.buffer_lint( | |
434 | rustc_lint_defs::builtin::DEPRECATED_CFG_ATTR_CRATE_TYPE_NAME, | |
435 | attr.span, | |
436 | ast::CRATE_NODE_ID, | |
437 | "`crate_name` within an `#![cfg_attr] attribute is deprecated`", | |
438 | ); | |
439 | } | |
440 | attr | |
60c5eb7d XL |
441 | } |
442 | ||
9fa01778 | 443 | /// Determines if a node with the given attributes should be included in this configuration. |
6a06907d | 444 | fn in_cfg(&self, attrs: &[Attribute]) -> bool { |
5099ac24 FG |
445 | attrs.iter().all(|attr| !is_cfg(attr) || self.cfg_true(attr)) |
446 | } | |
447 | ||
923072b8 | 448 | pub(crate) fn cfg_true(&self, attr: &Attribute) -> bool { |
5099ac24 FG |
449 | let meta_item = match validate_attr::parse_meta(&self.sess.parse_sess, attr) { |
450 | Ok(meta_item) => meta_item, | |
451 | Err(mut err) => { | |
452 | err.emit(); | |
cc61c64b | 453 | return true; |
8faf50e0 | 454 | } |
5099ac24 FG |
455 | }; |
456 | parse_cfg(&meta_item, &self.sess).map_or(true, |meta_item| { | |
5e7ed085 | 457 | attr::cfg_matches(&meta_item, &self.sess.parse_sess, self.lint_node_id, self.features) |
3157f602 XL |
458 | }) |
459 | } | |
92a42be0 | 460 | |
0531ce1d | 461 | /// If attributes are not allowed on expressions, emit an error for `attr` |
2b03887a | 462 | #[instrument(level = "trace", skip(self))] |
923072b8 | 463 | pub(crate) fn maybe_emit_expr_attr_err(&self, attr: &Attribute) { |
5869c6ff | 464 | if !self.features.map_or(true, |features| features.stmt_expr_attributes) { |
dfeec247 | 465 | let mut err = feature_err( |
3dfed10e | 466 | &self.sess.parse_sess, |
dfeec247 XL |
467 | sym::stmt_expr_attributes, |
468 | attr.span, | |
469 | "attributes on expressions are experimental", | |
470 | ); | |
0531ce1d | 471 | |
60c5eb7d | 472 | if attr.is_doc_comment() { |
0531ce1d | 473 | err.help("`///` is for documentation comments. For a plain comment, use `//`."); |
92a42be0 | 474 | } |
0531ce1d XL |
475 | |
476 | err.emit(); | |
92a42be0 SL |
477 | } |
478 | } | |
92a42be0 | 479 | |
2b03887a FG |
480 | #[instrument(level = "trace", skip(self))] |
481 | pub fn configure_expr(&self, expr: &mut P<ast::Expr>, method_receiver: bool) { | |
482 | if !method_receiver { | |
483 | for attr in expr.attrs.iter() { | |
484 | self.maybe_emit_expr_attr_err(attr); | |
485 | } | |
6a06907d | 486 | } |
3157f602 XL |
487 | |
488 | // If an expr is valid to cfg away it will have been removed by the | |
489 | // outer stmt or expression folder before descending in here. | |
490 | // Anything else is always required, and thus has to error out | |
491 | // in case of a cfg attr. | |
492 | // | |
9fa01778 XL |
493 | // N.B., this is intentionally not part of the visit_expr() function |
494 | // in order for filter_map_expr() to be able to avoid this check | |
94222f64 | 495 | if let Some(attr) = expr.attrs().iter().find(|a| is_cfg(*a)) { |
9c376795 | 496 | self.sess.emit_err(RemoveExprNotSupported { span: attr.span }); |
92a42be0 | 497 | } |
3157f602 | 498 | |
cdc7bbd5 XL |
499 | self.process_cfg_attrs(expr); |
500 | self.try_configure_tokens(&mut *expr); | |
92a42be0 | 501 | } |
92a42be0 SL |
502 | } |
503 | ||
17df50a5 | 504 | pub fn parse_cfg<'a>(meta_item: &'a MetaItem, sess: &Session) -> Option<&'a MetaItem> { |
17df50a5 XL |
505 | let span = meta_item.span; |
506 | match meta_item.meta_item_list() { | |
9c376795 FG |
507 | None => { |
508 | sess.emit_err(InvalidCfg::NotFollowedByParens { span }); | |
509 | None | |
510 | } | |
511 | Some([]) => { | |
512 | sess.emit_err(InvalidCfg::NoPredicate { span }); | |
513 | None | |
514 | } | |
515 | Some([_, .., l]) => { | |
516 | sess.emit_err(InvalidCfg::MultiplePredicates { span: l.span() }); | |
517 | None | |
518 | } | |
17df50a5 XL |
519 | Some([single]) => match single.meta_item() { |
520 | Some(meta_item) => Some(meta_item), | |
9c376795 FG |
521 | None => { |
522 | sess.emit_err(InvalidCfg::PredicateLiteral { span: single.span() }); | |
523 | None | |
524 | } | |
17df50a5 XL |
525 | }, |
526 | } | |
527 | } | |
528 | ||
94222f64 XL |
529 | fn is_cfg(attr: &Attribute) -> bool { |
530 | attr.has_name(sym::cfg) | |
92a42be0 | 531 | } |