Auto merge of #92473 - petrochenkov:ltrattr2, r=Aaron1011
expand: Pick `cfg`s and `cfg_attrs` one by one, like other attributes This is a rebase of https://github.com/rust-lang/rust/pull/83354, but without any language-changing parts ~(except for https://github.com/rust-lang/rust/pull/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 (https://github.com/rust-lang/rust/issues/83331) - Keeping macro backtraces for cfg attributes, or otherwise making them visible after expansion without keeping them in place literally (https://github.com/rust-lang/rust/pull/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`
This commit is contained in:
commit
fd20513f52
@ -328,6 +328,10 @@ impl<'a> StripUnconfigured<'a> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn process_cfg_attr(&self, attr: Attribute) -> Vec<Attribute> {
|
||||||
|
if attr.has_name(sym::cfg_attr) { self.expand_cfg_attr(attr, true) } else { vec![attr] }
|
||||||
|
}
|
||||||
|
|
||||||
/// Parse and expand a single `cfg_attr` attribute into a list of attributes
|
/// Parse and expand a single `cfg_attr` attribute into a list of attributes
|
||||||
/// when the configuration predicate is true, or otherwise expand into an
|
/// when the configuration predicate is true, or otherwise expand into an
|
||||||
/// empty list of attributes.
|
/// empty list of attributes.
|
||||||
@ -335,11 +339,7 @@ impl<'a> StripUnconfigured<'a> {
|
|||||||
/// Gives a compiler warning when the `cfg_attr` contains no attributes and
|
/// Gives a compiler warning when the `cfg_attr` contains no attributes and
|
||||||
/// is in the original source file. Gives a compiler error if the syntax of
|
/// is in the original source file. Gives a compiler error if the syntax of
|
||||||
/// the attribute is incorrect.
|
/// the attribute is incorrect.
|
||||||
fn process_cfg_attr(&self, attr: Attribute) -> Vec<Attribute> {
|
crate fn expand_cfg_attr(&self, attr: Attribute, recursive: bool) -> Vec<Attribute> {
|
||||||
if !attr.has_name(sym::cfg_attr) {
|
|
||||||
return vec![attr];
|
|
||||||
}
|
|
||||||
|
|
||||||
let (cfg_predicate, expanded_attrs) =
|
let (cfg_predicate, expanded_attrs) =
|
||||||
match rustc_parse::parse_cfg_attr(&attr, &self.sess.parse_sess) {
|
match rustc_parse::parse_cfg_attr(&attr, &self.sess.parse_sess) {
|
||||||
None => return vec![],
|
None => return vec![],
|
||||||
@ -348,95 +348,109 @@ impl<'a> StripUnconfigured<'a> {
|
|||||||
|
|
||||||
// Lint on zero attributes in source.
|
// Lint on zero attributes in source.
|
||||||
if expanded_attrs.is_empty() {
|
if expanded_attrs.is_empty() {
|
||||||
return vec![attr];
|
self.sess.parse_sess.buffer_lint(
|
||||||
|
rustc_lint_defs::builtin::UNUSED_ATTRIBUTES,
|
||||||
|
attr.span,
|
||||||
|
ast::CRATE_NODE_ID,
|
||||||
|
"`#[cfg_attr]` does not expand to any attributes",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if !attr::cfg_matches(&cfg_predicate, &self.sess.parse_sess, self.features) {
|
if !attr::cfg_matches(&cfg_predicate, &self.sess.parse_sess, self.features) {
|
||||||
return vec![];
|
return vec![];
|
||||||
}
|
}
|
||||||
|
|
||||||
// We call `process_cfg_attr` recursively in case there's a
|
if recursive {
|
||||||
// `cfg_attr` inside of another `cfg_attr`. E.g.
|
// We call `process_cfg_attr` recursively in case there's a
|
||||||
// `#[cfg_attr(false, cfg_attr(true, some_attr))]`.
|
// `cfg_attr` inside of another `cfg_attr`. E.g.
|
||||||
expanded_attrs
|
// `#[cfg_attr(false, cfg_attr(true, some_attr))]`.
|
||||||
.into_iter()
|
expanded_attrs
|
||||||
.flat_map(|(item, span)| {
|
.into_iter()
|
||||||
let orig_tokens = attr.tokens().to_tokenstream();
|
.flat_map(|item| self.process_cfg_attr(self.expand_cfg_attr_item(&attr, item)))
|
||||||
|
.collect()
|
||||||
|
} else {
|
||||||
|
expanded_attrs.into_iter().map(|item| self.expand_cfg_attr_item(&attr, item)).collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// We are taking an attribute of the form `#[cfg_attr(pred, attr)]`
|
fn expand_cfg_attr_item(
|
||||||
// and producing an attribute of the form `#[attr]`. We
|
&self,
|
||||||
// have captured tokens for `attr` itself, but we need to
|
attr: &Attribute,
|
||||||
// synthesize tokens for the wrapper `#` and `[]`, which
|
(item, item_span): (ast::AttrItem, Span),
|
||||||
// we do below.
|
) -> Attribute {
|
||||||
|
let orig_tokens = attr.tokens().to_tokenstream();
|
||||||
|
|
||||||
// Use the `#` in `#[cfg_attr(pred, attr)]` as the `#` token
|
// We are taking an attribute of the form `#[cfg_attr(pred, attr)]`
|
||||||
// for `attr` when we expand it to `#[attr]`
|
// and producing an attribute of the form `#[attr]`. We
|
||||||
let mut orig_trees = orig_tokens.trees();
|
// have captured tokens for `attr` itself, but we need to
|
||||||
let pound_token = match orig_trees.next().unwrap() {
|
// synthesize tokens for the wrapper `#` and `[]`, which
|
||||||
TokenTree::Token(token @ Token { kind: TokenKind::Pound, .. }) => token,
|
// we do below.
|
||||||
_ => panic!("Bad tokens for attribute {:?}", attr),
|
|
||||||
};
|
|
||||||
let pound_span = pound_token.span;
|
|
||||||
|
|
||||||
let mut trees = vec![(AttrAnnotatedTokenTree::Token(pound_token), Spacing::Alone)];
|
// Use the `#` in `#[cfg_attr(pred, attr)]` as the `#` token
|
||||||
if attr.style == AttrStyle::Inner {
|
// for `attr` when we expand it to `#[attr]`
|
||||||
// For inner attributes, we do the same thing for the `!` in `#![some_attr]`
|
let mut orig_trees = orig_tokens.trees();
|
||||||
let bang_token = match orig_trees.next().unwrap() {
|
let pound_token = match orig_trees.next().unwrap() {
|
||||||
TokenTree::Token(token @ Token { kind: TokenKind::Not, .. }) => token,
|
TokenTree::Token(token @ Token { kind: TokenKind::Pound, .. }) => token,
|
||||||
_ => panic!("Bad tokens for attribute {:?}", attr),
|
_ => panic!("Bad tokens for attribute {:?}", attr),
|
||||||
};
|
};
|
||||||
trees.push((AttrAnnotatedTokenTree::Token(bang_token), Spacing::Alone));
|
let pound_span = pound_token.span;
|
||||||
}
|
|
||||||
// We don't really have a good span to use for the syntheized `[]`
|
let mut trees = vec![(AttrAnnotatedTokenTree::Token(pound_token), Spacing::Alone)];
|
||||||
// in `#[attr]`, so just use the span of the `#` token.
|
if attr.style == AttrStyle::Inner {
|
||||||
let bracket_group = AttrAnnotatedTokenTree::Delimited(
|
// For inner attributes, we do the same thing for the `!` in `#![some_attr]`
|
||||||
DelimSpan::from_single(pound_span),
|
let bang_token = match orig_trees.next().unwrap() {
|
||||||
DelimToken::Bracket,
|
TokenTree::Token(token @ Token { kind: TokenKind::Not, .. }) => token,
|
||||||
item.tokens
|
_ => panic!("Bad tokens for attribute {:?}", attr),
|
||||||
.as_ref()
|
};
|
||||||
.unwrap_or_else(|| panic!("Missing tokens for {:?}", item))
|
trees.push((AttrAnnotatedTokenTree::Token(bang_token), Spacing::Alone));
|
||||||
.create_token_stream(),
|
}
|
||||||
);
|
// We don't really have a good span to use for the syntheized `[]`
|
||||||
trees.push((bracket_group, Spacing::Alone));
|
// in `#[attr]`, so just use the span of the `#` token.
|
||||||
let tokens = Some(LazyTokenStream::new(AttrAnnotatedTokenStream::new(trees)));
|
let bracket_group = AttrAnnotatedTokenTree::Delimited(
|
||||||
let attr = attr::mk_attr_from_item(item, tokens, attr.style, span);
|
DelimSpan::from_single(pound_span),
|
||||||
if attr.has_name(sym::crate_type) {
|
DelimToken::Bracket,
|
||||||
self.sess.parse_sess.buffer_lint(
|
item.tokens
|
||||||
rustc_lint_defs::builtin::DEPRECATED_CFG_ATTR_CRATE_TYPE_NAME,
|
.as_ref()
|
||||||
attr.span,
|
.unwrap_or_else(|| panic!("Missing tokens for {:?}", item))
|
||||||
ast::CRATE_NODE_ID,
|
.create_token_stream(),
|
||||||
"`crate_type` within an `#![cfg_attr] attribute is deprecated`",
|
);
|
||||||
);
|
trees.push((bracket_group, Spacing::Alone));
|
||||||
}
|
let tokens = Some(LazyTokenStream::new(AttrAnnotatedTokenStream::new(trees)));
|
||||||
if attr.has_name(sym::crate_name) {
|
let attr = attr::mk_attr_from_item(item, tokens, attr.style, item_span);
|
||||||
self.sess.parse_sess.buffer_lint(
|
if attr.has_name(sym::crate_type) {
|
||||||
rustc_lint_defs::builtin::DEPRECATED_CFG_ATTR_CRATE_TYPE_NAME,
|
self.sess.parse_sess.buffer_lint(
|
||||||
attr.span,
|
rustc_lint_defs::builtin::DEPRECATED_CFG_ATTR_CRATE_TYPE_NAME,
|
||||||
ast::CRATE_NODE_ID,
|
attr.span,
|
||||||
"`crate_name` within an `#![cfg_attr] attribute is deprecated`",
|
ast::CRATE_NODE_ID,
|
||||||
);
|
"`crate_type` within an `#![cfg_attr] attribute is deprecated`",
|
||||||
}
|
);
|
||||||
self.process_cfg_attr(attr)
|
}
|
||||||
})
|
if attr.has_name(sym::crate_name) {
|
||||||
.collect()
|
self.sess.parse_sess.buffer_lint(
|
||||||
|
rustc_lint_defs::builtin::DEPRECATED_CFG_ATTR_CRATE_TYPE_NAME,
|
||||||
|
attr.span,
|
||||||
|
ast::CRATE_NODE_ID,
|
||||||
|
"`crate_name` within an `#![cfg_attr] attribute is deprecated`",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
attr
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Determines if a node with the given attributes should be included in this configuration.
|
/// Determines if a node with the given attributes should be included in this configuration.
|
||||||
fn in_cfg(&self, attrs: &[Attribute]) -> bool {
|
fn in_cfg(&self, attrs: &[Attribute]) -> bool {
|
||||||
attrs.iter().all(|attr| {
|
attrs.iter().all(|attr| !is_cfg(attr) || self.cfg_true(attr))
|
||||||
if !is_cfg(attr) {
|
}
|
||||||
|
|
||||||
|
crate fn cfg_true(&self, attr: &Attribute) -> bool {
|
||||||
|
let meta_item = match validate_attr::parse_meta(&self.sess.parse_sess, attr) {
|
||||||
|
Ok(meta_item) => meta_item,
|
||||||
|
Err(mut err) => {
|
||||||
|
err.emit();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
let meta_item = match validate_attr::parse_meta(&self.sess.parse_sess, attr) {
|
};
|
||||||
Ok(meta_item) => meta_item,
|
parse_cfg(&meta_item, &self.sess).map_or(true, |meta_item| {
|
||||||
Err(mut err) => {
|
attr::cfg_matches(&meta_item, &self.sess.parse_sess, self.features)
|
||||||
err.emit();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
parse_cfg(&meta_item, &self.sess).map_or(true, |meta_item| {
|
|
||||||
attr::cfg_matches(&meta_item, &self.sess.parse_sess, self.features)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,7 +15,6 @@ use rustc_ast::{AssocItemKind, AstLike, AstLikeWrapper, AttrStyle, ExprKind, For
|
|||||||
use rustc_ast::{Inline, ItemKind, MacArgs, MacStmtStyle, MetaItemKind, ModKind, NestedMetaItem};
|
use rustc_ast::{Inline, ItemKind, MacArgs, MacStmtStyle, MetaItemKind, ModKind, NestedMetaItem};
|
||||||
use rustc_ast::{NodeId, PatKind, StmtKind, TyKind};
|
use rustc_ast::{NodeId, PatKind, StmtKind, TyKind};
|
||||||
use rustc_ast_pretty::pprust;
|
use rustc_ast_pretty::pprust;
|
||||||
use rustc_attr::is_builtin_attr;
|
|
||||||
use rustc_data_structures::map_in_place::MapInPlace;
|
use rustc_data_structures::map_in_place::MapInPlace;
|
||||||
use rustc_data_structures::sync::Lrc;
|
use rustc_data_structures::sync::Lrc;
|
||||||
use rustc_errors::{Applicability, PResult};
|
use rustc_errors::{Applicability, PResult};
|
||||||
@ -1014,6 +1013,9 @@ trait InvocationCollectorNode: AstLike {
|
|||||||
fn to_annotatable(self) -> Annotatable;
|
fn to_annotatable(self) -> Annotatable;
|
||||||
fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy;
|
fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy;
|
||||||
fn id(&mut self) -> &mut NodeId;
|
fn id(&mut self) -> &mut NodeId;
|
||||||
|
fn descr() -> &'static str {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
fn noop_flat_map<V: MutVisitor>(self, _visitor: &mut V) -> Self::OutputTy {
|
fn noop_flat_map<V: MutVisitor>(self, _visitor: &mut V) -> Self::OutputTy {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
}
|
||||||
@ -1477,6 +1479,9 @@ impl InvocationCollectorNode for P<ast::Expr> {
|
|||||||
fn id(&mut self) -> &mut NodeId {
|
fn id(&mut self) -> &mut NodeId {
|
||||||
&mut self.id
|
&mut self.id
|
||||||
}
|
}
|
||||||
|
fn descr() -> &'static str {
|
||||||
|
"an expression"
|
||||||
|
}
|
||||||
fn noop_visit<V: MutVisitor>(&mut self, visitor: &mut V) {
|
fn noop_visit<V: MutVisitor>(&mut self, visitor: &mut V) {
|
||||||
noop_visit_expr(self, visitor)
|
noop_visit_expr(self, visitor)
|
||||||
}
|
}
|
||||||
@ -1576,13 +1581,28 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
|
|||||||
) -> Option<(ast::Attribute, usize, Vec<ast::Path>)> {
|
) -> Option<(ast::Attribute, usize, Vec<ast::Path>)> {
|
||||||
let mut attr = None;
|
let mut attr = None;
|
||||||
|
|
||||||
|
let mut cfg_pos = None;
|
||||||
|
let mut attr_pos = None;
|
||||||
|
for (pos, attr) in item.attrs().iter().enumerate() {
|
||||||
|
if !attr.is_doc_comment() && !self.cx.expanded_inert_attrs.is_marked(attr) {
|
||||||
|
let name = attr.ident().map(|ident| ident.name);
|
||||||
|
if name == Some(sym::cfg) || name == Some(sym::cfg_attr) {
|
||||||
|
cfg_pos = Some(pos); // a cfg attr found, no need to search anymore
|
||||||
|
break;
|
||||||
|
} else if attr_pos.is_none()
|
||||||
|
&& !name.map_or(false, rustc_feature::is_builtin_attr_name)
|
||||||
|
{
|
||||||
|
attr_pos = Some(pos); // a non-cfg attr found, still may find a cfg attr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
item.visit_attrs(|attrs| {
|
item.visit_attrs(|attrs| {
|
||||||
attr = attrs
|
attr = Some(match (cfg_pos, attr_pos) {
|
||||||
.iter()
|
(Some(pos), _) => (attrs.remove(pos), pos, Vec::new()),
|
||||||
.position(|a| !self.cx.expanded_inert_attrs.is_marked(a) && !is_builtin_attr(a))
|
(_, Some(pos)) => {
|
||||||
.map(|attr_pos| {
|
let attr = attrs.remove(pos);
|
||||||
let attr = attrs.remove(attr_pos);
|
let following_derives = attrs[pos..]
|
||||||
let following_derives = attrs[attr_pos..]
|
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|a| a.has_name(sym::derive))
|
.filter(|a| a.has_name(sym::derive))
|
||||||
.flat_map(|a| a.meta_item_list().unwrap_or_default())
|
.flat_map(|a| a.meta_item_list().unwrap_or_default())
|
||||||
@ -1596,17 +1616,15 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
|
|||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
(attr, attr_pos, following_derives)
|
(attr, pos, following_derives)
|
||||||
})
|
}
|
||||||
|
_ => return,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
attr
|
attr
|
||||||
}
|
}
|
||||||
|
|
||||||
fn configure<T: AstLike>(&mut self, node: T) -> Option<T> {
|
|
||||||
self.cfg.configure(node)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Detect use of feature-gated or invalid attributes on macro invocations
|
// Detect use of feature-gated or invalid attributes on macro invocations
|
||||||
// since they will not be detected after macro expansion.
|
// since they will not be detected after macro expansion.
|
||||||
fn check_attributes(&self, attrs: &[ast::Attribute], call: &ast::MacCall) {
|
fn check_attributes(&self, attrs: &[ast::Attribute], call: &ast::MacCall) {
|
||||||
@ -1653,28 +1671,71 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn expand_cfg_true(
|
||||||
|
&mut self,
|
||||||
|
node: &mut impl AstLike,
|
||||||
|
attr: ast::Attribute,
|
||||||
|
pos: usize,
|
||||||
|
) -> bool {
|
||||||
|
let res = self.cfg.cfg_true(&attr);
|
||||||
|
if res {
|
||||||
|
// FIXME: `cfg(TRUE)` attributes do not currently remove themselves during expansion,
|
||||||
|
// and some tools like rustdoc and clippy rely on that. Find a way to remove them
|
||||||
|
// while keeping the tools working.
|
||||||
|
self.cx.expanded_inert_attrs.mark(&attr);
|
||||||
|
node.visit_attrs(|attrs| attrs.insert(pos, attr));
|
||||||
|
}
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expand_cfg_attr(&self, node: &mut impl AstLike, attr: ast::Attribute, pos: usize) {
|
||||||
|
node.visit_attrs(|attrs| {
|
||||||
|
attrs.splice(pos..pos, self.cfg.expand_cfg_attr(attr, false));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
fn flat_map_node<Node: InvocationCollectorNode<OutputTy: Default>>(
|
fn flat_map_node<Node: InvocationCollectorNode<OutputTy: Default>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
node: Node,
|
mut node: Node,
|
||||||
) -> Node::OutputTy {
|
) -> Node::OutputTy {
|
||||||
let mut node = configure!(self, node);
|
loop {
|
||||||
|
return match self.take_first_attr(&mut node) {
|
||||||
if let Some(attr) = self.take_first_attr(&mut node) {
|
Some((attr, pos, derives)) => match attr.name_or_empty() {
|
||||||
Node::pre_flat_map_node_collect_attr(&self.cfg, &attr.0);
|
sym::cfg => {
|
||||||
self.collect_attr(attr, node.to_annotatable(), Node::KIND).make_ast::<Node>()
|
if self.expand_cfg_true(&mut node, attr, pos) {
|
||||||
} else if node.is_mac_call() {
|
continue;
|
||||||
let (mac, attrs, add_semicolon) = node.take_mac_call();
|
}
|
||||||
self.check_attributes(&attrs, &mac);
|
Default::default()
|
||||||
let mut res = self.collect_bang(mac, Node::KIND).make_ast::<Node>();
|
}
|
||||||
Node::post_flat_map_node_collect_bang(&mut res, add_semicolon);
|
sym::cfg_attr => {
|
||||||
res
|
self.expand_cfg_attr(&mut node, attr, pos);
|
||||||
} else {
|
continue;
|
||||||
match Node::wrap_flat_map_node_noop_flat_map(node, self, |mut node, this| {
|
}
|
||||||
assign_id!(this, node.id(), || node.noop_flat_map(this))
|
_ => {
|
||||||
}) {
|
Node::pre_flat_map_node_collect_attr(&self.cfg, &attr);
|
||||||
Ok(output) => output,
|
self.collect_attr((attr, pos, derives), node.to_annotatable(), Node::KIND)
|
||||||
Err(node) => self.flat_map_node(node),
|
.make_ast::<Node>()
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
None if node.is_mac_call() => {
|
||||||
|
let (mac, attrs, add_semicolon) = node.take_mac_call();
|
||||||
|
self.check_attributes(&attrs, &mac);
|
||||||
|
let mut res = self.collect_bang(mac, Node::KIND).make_ast::<Node>();
|
||||||
|
Node::post_flat_map_node_collect_bang(&mut res, add_semicolon);
|
||||||
|
res
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
match Node::wrap_flat_map_node_noop_flat_map(node, self, |mut node, this| {
|
||||||
|
assign_id!(this, node.id(), || node.noop_flat_map(this))
|
||||||
|
}) {
|
||||||
|
Ok(output) => output,
|
||||||
|
Err(returned_node) => {
|
||||||
|
node = returned_node;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1682,19 +1743,40 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
node: &mut Node,
|
node: &mut Node,
|
||||||
) {
|
) {
|
||||||
if let Some(attr) = self.take_first_attr(node) {
|
loop {
|
||||||
visit_clobber(node, |node| {
|
return match self.take_first_attr(node) {
|
||||||
self.collect_attr(attr, node.to_annotatable(), Node::KIND).make_ast::<Node>()
|
Some((attr, pos, derives)) => match attr.name_or_empty() {
|
||||||
})
|
sym::cfg => {
|
||||||
} else if node.is_mac_call() {
|
let span = attr.span;
|
||||||
visit_clobber(node, |node| {
|
if self.expand_cfg_true(node, attr, pos) {
|
||||||
// Do not clobber unless it's actually a macro (uncommon case).
|
continue;
|
||||||
let (mac, attrs, _) = node.take_mac_call();
|
}
|
||||||
self.check_attributes(&attrs, &mac);
|
let msg =
|
||||||
self.collect_bang(mac, Node::KIND).make_ast::<Node>()
|
format!("removing {} is not supported in this position", Node::descr());
|
||||||
})
|
self.cx.span_err(span, &msg);
|
||||||
} else {
|
continue;
|
||||||
assign_id!(self, node.id(), || node.noop_visit(self))
|
}
|
||||||
|
sym::cfg_attr => {
|
||||||
|
self.expand_cfg_attr(node, attr, pos);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
_ => visit_clobber(node, |node| {
|
||||||
|
self.collect_attr((attr, pos, derives), node.to_annotatable(), Node::KIND)
|
||||||
|
.make_ast::<Node>()
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
None if node.is_mac_call() => {
|
||||||
|
visit_clobber(node, |node| {
|
||||||
|
// Do not clobber unless it's actually a macro (uncommon case).
|
||||||
|
let (mac, attrs, _) = node.take_mac_call();
|
||||||
|
self.check_attributes(&attrs, &mac);
|
||||||
|
self.collect_bang(mac, Node::KIND).make_ast::<Node>()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
assign_id!(self, node.id(), || node.noop_visit(self))
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1750,7 +1832,7 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
|
|||||||
self.flat_map_node(node)
|
self.flat_map_node(node)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn flat_map_stmt(&mut self, node: ast::Stmt) -> SmallVec<[ast::Stmt; 1]> {
|
fn flat_map_stmt(&mut self, mut node: ast::Stmt) -> SmallVec<[ast::Stmt; 1]> {
|
||||||
// FIXME: invocations in semicolon-less expressions positions are expanded as expressions,
|
// FIXME: invocations in semicolon-less expressions positions are expanded as expressions,
|
||||||
// changing that requires some compatibility measures.
|
// changing that requires some compatibility measures.
|
||||||
if node.is_expr() {
|
if node.is_expr() {
|
||||||
@ -1761,7 +1843,6 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
|
|||||||
// `SEMICOLON_IN_EXPRESSIONS_FROM_MACROS` lint if needed.
|
// `SEMICOLON_IN_EXPRESSIONS_FROM_MACROS` lint if needed.
|
||||||
// See #78991 for an investigation of treating macros in this position
|
// See #78991 for an investigation of treating macros in this position
|
||||||
// as statements, rather than expressions, during parsing.
|
// as statements, rather than expressions, during parsing.
|
||||||
let mut node = configure!(self, node);
|
|
||||||
return match &node.kind {
|
return match &node.kind {
|
||||||
StmtKind::Expr(expr)
|
StmtKind::Expr(expr)
|
||||||
if matches!(**expr, ast::Expr { kind: ExprKind::MacCall(..), .. }) =>
|
if matches!(**expr, ast::Expr { kind: ExprKind::MacCall(..), .. }) =>
|
||||||
@ -1793,7 +1874,10 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn visit_expr(&mut self, node: &mut P<ast::Expr>) {
|
fn visit_expr(&mut self, node: &mut P<ast::Expr>) {
|
||||||
self.cfg.configure_expr(node);
|
// FIXME: Feature gating is performed inconsistently between `Expr` and `OptExpr`.
|
||||||
|
if let Some(attr) = node.attrs.first() {
|
||||||
|
self.cfg.maybe_emit_expr_attr_err(attr);
|
||||||
|
}
|
||||||
self.visit_node(node)
|
self.visit_node(node)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,7 +135,6 @@ impl CheckAttrVisitor<'_> {
|
|||||||
}
|
}
|
||||||
sym::macro_use | sym::macro_escape => self.check_macro_use(hir_id, attr, target),
|
sym::macro_use | sym::macro_escape => self.check_macro_use(hir_id, attr, target),
|
||||||
sym::path => self.check_generic_attr(hir_id, attr, target, &[Target::Mod]),
|
sym::path => self.check_generic_attr(hir_id, attr, target, &[Target::Mod]),
|
||||||
sym::cfg_attr => self.check_cfg_attr(hir_id, attr),
|
|
||||||
sym::plugin_registrar => self.check_plugin_registrar(hir_id, attr, target),
|
sym::plugin_registrar => self.check_plugin_registrar(hir_id, attr, target),
|
||||||
sym::macro_export => self.check_macro_export(hir_id, attr, target),
|
sym::macro_export => self.check_macro_export(hir_id, attr, target),
|
||||||
sym::ignore | sym::should_panic | sym::proc_macro_derive => {
|
sym::ignore | sym::should_panic | sym::proc_macro_derive => {
|
||||||
@ -1842,16 +1841,6 @@ impl CheckAttrVisitor<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_cfg_attr(&self, hir_id: HirId, attr: &Attribute) {
|
|
||||||
if let Some((_, attrs)) = rustc_parse::parse_cfg_attr(&attr, &self.tcx.sess.parse_sess) {
|
|
||||||
if attrs.is_empty() {
|
|
||||||
self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
|
|
||||||
lint.build("`#[cfg_attr]` does not expand to any attributes").emit();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_plugin_registrar(&self, hir_id: HirId, attr: &Attribute, target: Target) {
|
fn check_plugin_registrar(&self, hir_id: HirId, attr: &Attribute, target: Target) {
|
||||||
if target != Target::Fn {
|
if target != Target::Fn {
|
||||||
self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
|
self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
#[cfg(target_abi = "x")] //~ ERROR `cfg(target_abi)` is experimental
|
#[cfg(target_abi = "x")] //~ ERROR `cfg(target_abi)` is experimental
|
||||||
#[cfg_attr(target_abi = "x", x)] //~ ERROR `cfg(target_abi)` is experimental
|
|
||||||
struct Foo(u64, u64);
|
struct Foo(u64, u64);
|
||||||
|
|
||||||
|
#[cfg_attr(target_abi = "x", x)] //~ ERROR `cfg(target_abi)` is experimental
|
||||||
|
struct Bar(u64, u64);
|
||||||
|
|
||||||
#[cfg(not(any(all(target_abi = "x"))))] //~ ERROR `cfg(target_abi)` is experimental
|
#[cfg(not(any(all(target_abi = "x"))))] //~ ERROR `cfg(target_abi)` is experimental
|
||||||
fn foo() {}
|
fn foo() {}
|
||||||
|
|
||||||
|
@ -1,12 +1,3 @@
|
|||||||
error[E0658]: `cfg(target_abi)` is experimental and subject to change
|
|
||||||
--> $DIR/feature-gate-cfg-target-abi.rs:2:12
|
|
||||||
|
|
|
||||||
LL | #[cfg_attr(target_abi = "x", x)]
|
|
||||||
| ^^^^^^^^^^^^^^^^
|
|
||||||
|
|
|
||||||
= note: see issue #80970 <https://github.com/rust-lang/rust/issues/80970> for more information
|
|
||||||
= help: add `#![feature(cfg_target_abi)]` to the crate attributes to enable
|
|
||||||
|
|
||||||
error[E0658]: `cfg(target_abi)` is experimental and subject to change
|
error[E0658]: `cfg(target_abi)` is experimental and subject to change
|
||||||
--> $DIR/feature-gate-cfg-target-abi.rs:1:7
|
--> $DIR/feature-gate-cfg-target-abi.rs:1:7
|
||||||
|
|
|
|
||||||
@ -17,7 +8,16 @@ LL | #[cfg(target_abi = "x")]
|
|||||||
= help: add `#![feature(cfg_target_abi)]` to the crate attributes to enable
|
= help: add `#![feature(cfg_target_abi)]` to the crate attributes to enable
|
||||||
|
|
||||||
error[E0658]: `cfg(target_abi)` is experimental and subject to change
|
error[E0658]: `cfg(target_abi)` is experimental and subject to change
|
||||||
--> $DIR/feature-gate-cfg-target-abi.rs:5:19
|
--> $DIR/feature-gate-cfg-target-abi.rs:4:12
|
||||||
|
|
|
||||||
|
LL | #[cfg_attr(target_abi = "x", x)]
|
||||||
|
| ^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: see issue #80970 <https://github.com/rust-lang/rust/issues/80970> for more information
|
||||||
|
= help: add `#![feature(cfg_target_abi)]` to the crate attributes to enable
|
||||||
|
|
||||||
|
error[E0658]: `cfg(target_abi)` is experimental and subject to change
|
||||||
|
--> $DIR/feature-gate-cfg-target-abi.rs:7:19
|
||||||
|
|
|
|
||||||
LL | #[cfg(not(any(all(target_abi = "x"))))]
|
LL | #[cfg(not(any(all(target_abi = "x"))))]
|
||||||
| ^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^
|
||||||
@ -26,7 +26,7 @@ LL | #[cfg(not(any(all(target_abi = "x"))))]
|
|||||||
= help: add `#![feature(cfg_target_abi)]` to the crate attributes to enable
|
= help: add `#![feature(cfg_target_abi)]` to the crate attributes to enable
|
||||||
|
|
||||||
error[E0658]: `cfg(target_abi)` is experimental and subject to change
|
error[E0658]: `cfg(target_abi)` is experimental and subject to change
|
||||||
--> $DIR/feature-gate-cfg-target-abi.rs:9:10
|
--> $DIR/feature-gate-cfg-target-abi.rs:11:10
|
||||||
|
|
|
|
||||||
LL | cfg!(target_abi = "x");
|
LL | cfg!(target_abi = "x");
|
||||||
| ^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^
|
||||||
|
@ -4,6 +4,4 @@
|
|||||||
fn main() {
|
fn main() {
|
||||||
let _ = #[cfg_eval] #[cfg(FALSE)] 0;
|
let _ = #[cfg_eval] #[cfg(FALSE)] 0;
|
||||||
//~^ ERROR removing an expression is not supported in this position
|
//~^ ERROR removing an expression is not supported in this position
|
||||||
//~| ERROR removing an expression is not supported in this position
|
|
||||||
//~| ERROR removing an expression is not supported in this position
|
|
||||||
}
|
}
|
||||||
|
@ -4,17 +4,5 @@ error: removing an expression is not supported in this position
|
|||||||
LL | let _ = #[cfg_eval] #[cfg(FALSE)] 0;
|
LL | let _ = #[cfg_eval] #[cfg(FALSE)] 0;
|
||||||
| ^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^
|
||||||
|
|
||||||
error: removing an expression is not supported in this position
|
error: aborting due to previous error
|
||||||
--> $DIR/cfg-eval-fail.rs:5:25
|
|
||||||
|
|
|
||||||
LL | let _ = #[cfg_eval] #[cfg(FALSE)] 0;
|
|
||||||
| ^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
error: removing an expression is not supported in this position
|
|
||||||
--> $DIR/cfg-eval-fail.rs:5:25
|
|
||||||
|
|
|
||||||
LL | let _ = #[cfg_eval] #[cfg(FALSE)] 0;
|
|
||||||
| ^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
error: aborting due to 3 previous errors
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user