7999: Handle `cfg_attr` gating multiple attributes r=jonas-schievink a=jonas-schievink

Apparently `#[cfg_attr(cfg_expr, attr1, attr2)]` is valid, so let's add support for that.

bors r+

Co-authored-by: Jonas Schievink <jonasschievink@gmail.com>
This commit is contained in:
bors[bot] 2021-03-13 17:20:16 +00:00 committed by GitHub
commit be7a31fbd6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 22 additions and 29 deletions

View File

@ -9,6 +9,7 @@ use hir_expand::{hygiene::Hygiene, name::AsName, AstId, InFile};
use itertools::Itertools;
use la_arena::ArenaMap;
use mbe::ast_to_token_tree;
use smallvec::{smallvec, SmallVec};
use syntax::{
ast::{self, AstNode, AttrsOwner},
match_ast, AstToken, SmolStr, SyntaxNode,
@ -134,53 +135,42 @@ impl RawAttrs {
let crate_graph = db.crate_graph();
let new_attrs = self
.iter()
.filter_map(|attr| {
.flat_map(|attr| -> SmallVec<[_; 1]> {
let attr = attr.clone();
let is_cfg_attr =
attr.path.as_ident().map_or(false, |name| *name == hir_expand::name![cfg_attr]);
if !is_cfg_attr {
return Some(attr);
return smallvec![attr];
}
let subtree = match &attr.input {
Some(AttrInput::TokenTree(it)) => it,
_ => return Some(attr),
_ => return smallvec![attr],
};
// Input subtree is: `(cfg, attr)`
// Split it up into a `cfg` and an `attr` subtree.
// Input subtree is: `(cfg, $(attr),+)`
// Split it up into a `cfg` subtree and the `attr` subtrees.
// FIXME: There should be a common API for this.
let mut saw_comma = false;
let (mut cfg, attr): (Vec<_>, Vec<_>) =
subtree.clone().token_trees.into_iter().partition(|tree| {
if saw_comma {
return false;
}
match tree {
tt::TokenTree::Leaf(tt::Leaf::Punct(p)) if p.char == ',' => {
saw_comma = true;
}
_ => {}
}
true
});
cfg.pop(); // `,` ends up in here
let attr = Subtree { delimiter: None, token_trees: attr };
let cfg = Subtree { delimiter: subtree.delimiter, token_trees: cfg };
let mut parts = subtree.token_trees.split(
|tt| matches!(tt, tt::TokenTree::Leaf(tt::Leaf::Punct(p)) if p.char == ','),
);
let cfg = parts.next().unwrap();
let cfg = Subtree { delimiter: subtree.delimiter, token_trees: cfg.to_vec() };
let cfg = CfgExpr::parse(&cfg);
let attrs = parts.filter(|a| !a.is_empty()).filter_map(|attr| {
let tree = Subtree { delimiter: None, token_trees: attr.to_vec() };
let attr = ast::Attr::parse(&format!("#[{}]", tree)).ok()?;
let hygiene = Hygiene::new_unhygienic(); // FIXME
Attr::from_src(attr, &hygiene)
});
let cfg_options = &crate_graph[krate].cfg_options;
if cfg_options.check(&cfg) == Some(false) {
None
smallvec![]
} else {
cov_mark::hit!(cfg_attr_active);
let attr = ast::Attr::parse(&format!("#[{}]", attr)).ok()?;
let hygiene = Hygiene::new_unhygienic(); // FIXME
Attr::from_src(attr, &hygiene)
attrs.collect()
}
})
.collect();

View File

@ -149,6 +149,9 @@ fn inactive_via_cfg_attr() {
#[cfg_attr(not(never), cfg(not(no)))] fn f() {}
#[cfg_attr(never, cfg(no))] fn g() {}
#[cfg_attr(not(never), inline, cfg(no))] fn h() {}
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: no is disabled
"#,
);
}