mirror of
https://github.com/rust-lang/rust.git
synced 2025-04-12 20:16:49 +00:00
Merge #6934
6934: Implement `cfg_attr` handling r=jonas-schievink a=jonas-schievink Part of https://github.com/rust-analyzer/rust-analyzer/issues/5548 Co-authored-by: Jonas Schievink <jonasschievink@gmail.com>
This commit is contained in:
commit
c073e4f6ba
@ -12,6 +12,7 @@ use syntax::{
|
||||
ast::{self, AstNode, AttrsOwner},
|
||||
match_ast, AstToken, SmolStr, SyntaxNode,
|
||||
};
|
||||
use test_utils::mark;
|
||||
use tt::Subtree;
|
||||
|
||||
use crate::{
|
||||
@ -122,9 +123,69 @@ impl RawAttrs {
|
||||
}
|
||||
|
||||
/// Processes `cfg_attr`s, returning the resulting semantic `Attrs`.
|
||||
pub(crate) fn filter(self, _db: &dyn DefDatabase, _krate: CrateId) -> Attrs {
|
||||
// FIXME actually implement this
|
||||
Attrs(self)
|
||||
pub(crate) fn filter(self, db: &dyn DefDatabase, krate: CrateId) -> Attrs {
|
||||
let has_cfg_attrs = self.iter().any(|attr| {
|
||||
attr.path.as_ident().map_or(false, |name| *name == hir_expand::name![cfg_attr])
|
||||
});
|
||||
if !has_cfg_attrs {
|
||||
return Attrs(self);
|
||||
}
|
||||
|
||||
let crate_graph = db.crate_graph();
|
||||
let new_attrs = self
|
||||
.iter()
|
||||
.filter_map(|attr| {
|
||||
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);
|
||||
}
|
||||
|
||||
let subtree = match &attr.input {
|
||||
Some(AttrInput::TokenTree(it)) => it,
|
||||
_ => return Some(attr),
|
||||
};
|
||||
|
||||
// Input subtree is: `(cfg, attr)`
|
||||
// Split it up into a `cfg` and an `attr` subtree.
|
||||
// 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 cfg = CfgExpr::parse(&cfg);
|
||||
|
||||
let cfg_options = &crate_graph[krate].cfg_options;
|
||||
if cfg_options.check(&cfg) == Some(false) {
|
||||
None
|
||||
} else {
|
||||
mark::hit!(cfg_attr_active);
|
||||
|
||||
let attr = ast::Attr::parse(&format!("#[{}]", attr)).ok()?;
|
||||
let hygiene = Hygiene::new_unhygienic(); // FIXME
|
||||
Attr::from_src(attr, &hygiene)
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
Attrs(RawAttrs { entries: Some(new_attrs) })
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
use base_db::fixture::WithFixture;
|
||||
use test_utils::mark;
|
||||
|
||||
use crate::test_db::TestDB;
|
||||
|
||||
@ -119,3 +120,20 @@ fn inactive_item() {
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
/// Tests that `cfg` attributes behind `cfg_attr` is handled properly.
|
||||
#[test]
|
||||
fn inactive_via_cfg_attr() {
|
||||
mark::check!(cfg_attr_active);
|
||||
check_diagnostics(
|
||||
r#"
|
||||
//- /lib.rs
|
||||
#[cfg_attr(not(never), cfg(no))] fn f() {}
|
||||
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: no is disabled
|
||||
|
||||
#[cfg_attr(not(never), cfg(not(no)))] fn f() {}
|
||||
|
||||
#[cfg_attr(never, cfg(no))] fn g() {}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
@ -153,6 +153,7 @@ pub mod known {
|
||||
// Special names
|
||||
macro_rules,
|
||||
doc,
|
||||
cfg_attr,
|
||||
// Components of known path (value or mod name)
|
||||
std,
|
||||
core,
|
||||
|
@ -133,6 +133,10 @@ pub(crate) mod fragments {
|
||||
|
||||
m.complete(p, MACRO_STMTS);
|
||||
}
|
||||
|
||||
pub(crate) fn attr(p: &mut Parser) {
|
||||
attributes::outer_attrs(p)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn reparser(
|
||||
|
@ -99,6 +99,8 @@ pub enum FragmentKind {
|
||||
// FIXME: use separate fragment kinds for macro inputs and outputs?
|
||||
Items,
|
||||
Statements,
|
||||
|
||||
Attr,
|
||||
}
|
||||
|
||||
pub fn parse_fragment(
|
||||
@ -118,6 +120,7 @@ pub fn parse_fragment(
|
||||
FragmentKind::Statement => grammar::fragments::stmt,
|
||||
FragmentKind::Items => grammar::fragments::macro_items,
|
||||
FragmentKind::Statements => grammar::fragments::macro_stmts,
|
||||
FragmentKind::Attr => grammar::fragments::attr,
|
||||
};
|
||||
parse_from_tokens(token_source, tree_sink, parser)
|
||||
}
|
||||
|
@ -205,6 +205,13 @@ impl ast::Type {
|
||||
}
|
||||
}
|
||||
|
||||
impl ast::Attr {
|
||||
/// Returns `text`, parsed as an attribute, but only if it has no errors.
|
||||
pub fn parse(text: &str) -> Result<Self, ()> {
|
||||
parsing::parse_text_fragment(text, parser::FragmentKind::Attr)
|
||||
}
|
||||
}
|
||||
|
||||
/// Matches a `SyntaxNode` against an `ast` type.
|
||||
///
|
||||
/// # Example:
|
||||
|
Loading…
Reference in New Issue
Block a user