Skip to content

Commit fd20513

Browse files
committed
Auto merge of rust-lang#92473 - petrochenkov:ltrattr2, r=Aaron1011
expand: Pick `cfg`s and `cfg_attrs` one by one, like other attributes This is a rebase of rust-lang#83354, but without any language-changing parts ~(except for rust-lang#84110, i.e. the attribute expansion order is the same. This is a pre-requisite for any other changes making cfg attributes closer to regular macro attributes - Possibly changing their expansion order (rust-lang#83331) - Keeping macro backtraces for cfg attributes, or otherwise making them visible after expansion without keeping them in place literally (rust-lang#84110). Two exceptions to the "one by one" behavior are: - cfgs eagerly expanded by `derive` and `cfg_eval`, they are still expanded in a batch, that's by design. - cfgs at the crate root, they are currently expanded not during the main expansion pass, but before that, during `#![feature]` collection. I'll try to disentangle that logic later in a separate PR. r? `@Aaron1011`
2 parents 1fbd6ae + e87ce7a commit fd20513

File tree

7 files changed

+240
-165
lines changed

7 files changed

+240
-165
lines changed

compiler/rustc_expand/src/config.rs

+96-82
Original file line numberDiff line numberDiff line change
@@ -328,18 +328,18 @@ impl<'a> StripUnconfigured<'a> {
328328
});
329329
}
330330

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+
331335
/// Parse and expand a single `cfg_attr` attribute into a list of attributes
332336
/// when the configuration predicate is true, or otherwise expand into an
333337
/// empty list of attributes.
334338
///
335339
/// Gives a compiler warning when the `cfg_attr` contains no attributes and
336340
/// is in the original source file. Gives a compiler error if the syntax of
337341
/// the attribute is incorrect.
338-
fn process_cfg_attr(&self, attr: Attribute) -> Vec<Attribute> {
339-
if !attr.has_name(sym::cfg_attr) {
340-
return vec![attr];
341-
}
342-
342+
crate fn expand_cfg_attr(&self, attr: Attribute, recursive: bool) -> Vec<Attribute> {
343343
let (cfg_predicate, expanded_attrs) =
344344
match rustc_parse::parse_cfg_attr(&attr, &self.sess.parse_sess) {
345345
None => return vec![],
@@ -348,95 +348,109 @@ impl<'a> StripUnconfigured<'a> {
348348

349349
// Lint on zero attributes in source.
350350
if expanded_attrs.is_empty() {
351-
return vec![attr];
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+
);
352357
}
353358

354359
if !attr::cfg_matches(&cfg_predicate, &self.sess.parse_sess, self.features) {
355360
return vec![];
356361
}
357362

358-
// We call `process_cfg_attr` recursively in case there's a
359-
// `cfg_attr` inside of another `cfg_attr`. E.g.
360-
// `#[cfg_attr(false, cfg_attr(true, some_attr))]`.
361-
expanded_attrs
362-
.into_iter()
363-
.flat_map(|(item, span)| {
364-
let orig_tokens = attr.tokens().to_tokenstream();
365-
366-
// We are taking an attribute of the form `#[cfg_attr(pred, attr)]`
367-
// and producing an attribute of the form `#[attr]`. We
368-
// have captured tokens for `attr` itself, but we need to
369-
// synthesize tokens for the wrapper `#` and `[]`, which
370-
// we do below.
371-
372-
// Use the `#` in `#[cfg_attr(pred, attr)]` as the `#` token
373-
// for `attr` when we expand it to `#[attr]`
374-
let mut orig_trees = orig_tokens.trees();
375-
let pound_token = match orig_trees.next().unwrap() {
376-
TokenTree::Token(token @ Token { kind: TokenKind::Pound, .. }) => token,
377-
_ => panic!("Bad tokens for attribute {:?}", attr),
378-
};
379-
let pound_span = pound_token.span;
380-
381-
let mut trees = vec![(AttrAnnotatedTokenTree::Token(pound_token), Spacing::Alone)];
382-
if attr.style == AttrStyle::Inner {
383-
// For inner attributes, we do the same thing for the `!` in `#![some_attr]`
384-
let bang_token = match orig_trees.next().unwrap() {
385-
TokenTree::Token(token @ Token { kind: TokenKind::Not, .. }) => token,
386-
_ => panic!("Bad tokens for attribute {:?}", attr),
387-
};
388-
trees.push((AttrAnnotatedTokenTree::Token(bang_token), Spacing::Alone));
389-
}
390-
// We don't really have a good span to use for the syntheized `[]`
391-
// in `#[attr]`, so just use the span of the `#` token.
392-
let bracket_group = AttrAnnotatedTokenTree::Delimited(
393-
DelimSpan::from_single(pound_span),
394-
DelimToken::Bracket,
395-
item.tokens
396-
.as_ref()
397-
.unwrap_or_else(|| panic!("Missing tokens for {:?}", item))
398-
.create_token_stream(),
399-
);
400-
trees.push((bracket_group, Spacing::Alone));
401-
let tokens = Some(LazyTokenStream::new(AttrAnnotatedTokenStream::new(trees)));
402-
let attr = attr::mk_attr_from_item(item, tokens, attr.style, span);
403-
if attr.has_name(sym::crate_type) {
404-
self.sess.parse_sess.buffer_lint(
405-
rustc_lint_defs::builtin::DEPRECATED_CFG_ATTR_CRATE_TYPE_NAME,
406-
attr.span,
407-
ast::CRATE_NODE_ID,
408-
"`crate_type` within an `#![cfg_attr] attribute is deprecated`",
409-
);
410-
}
411-
if attr.has_name(sym::crate_name) {
412-
self.sess.parse_sess.buffer_lint(
413-
rustc_lint_defs::builtin::DEPRECATED_CFG_ATTR_CRATE_TYPE_NAME,
414-
attr.span,
415-
ast::CRATE_NODE_ID,
416-
"`crate_name` within an `#![cfg_attr] attribute is deprecated`",
417-
);
418-
}
419-
self.process_cfg_attr(attr)
420-
})
421-
.collect()
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
422437
}
423438

424439
/// Determines if a node with the given attributes should be included in this configuration.
425440
fn in_cfg(&self, attrs: &[Attribute]) -> bool {
426-
attrs.iter().all(|attr| {
427-
if !is_cfg(attr) {
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();
428449
return true;
429450
}
430-
let meta_item = match validate_attr::parse_meta(&self.sess.parse_sess, attr) {
431-
Ok(meta_item) => meta_item,
432-
Err(mut err) => {
433-
err.emit();
434-
return true;
435-
}
436-
};
437-
parse_cfg(&meta_item, &self.sess).map_or(true, |meta_item| {
438-
attr::cfg_matches(&meta_item, &self.sess.parse_sess, self.features)
439-
})
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)
440454
})
441455
}
442456

0 commit comments

Comments
 (0)