mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-11-26 22:03:11 +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
6 changed files with 97 additions and 3 deletions
|
|
@ -12,6 +12,7 @@ use syntax::{
|
||||||
ast::{self, AstNode, AttrsOwner},
|
ast::{self, AstNode, AttrsOwner},
|
||||||
match_ast, AstToken, SmolStr, SyntaxNode,
|
match_ast, AstToken, SmolStr, SyntaxNode,
|
||||||
};
|
};
|
||||||
|
use test_utils::mark;
|
||||||
use tt::Subtree;
|
use tt::Subtree;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|
@ -122,9 +123,69 @@ impl RawAttrs {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Processes `cfg_attr`s, returning the resulting semantic `Attrs`.
|
/// Processes `cfg_attr`s, returning the resulting semantic `Attrs`.
|
||||||
pub(crate) fn filter(self, _db: &dyn DefDatabase, _krate: CrateId) -> Attrs {
|
pub(crate) fn filter(self, db: &dyn DefDatabase, krate: CrateId) -> Attrs {
|
||||||
// FIXME actually implement this
|
let has_cfg_attrs = self.iter().any(|attr| {
|
||||||
Attrs(self)
|
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 base_db::fixture::WithFixture;
|
||||||
|
use test_utils::mark;
|
||||||
|
|
||||||
use crate::test_db::TestDB;
|
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
|
// Special names
|
||||||
macro_rules,
|
macro_rules,
|
||||||
doc,
|
doc,
|
||||||
|
cfg_attr,
|
||||||
// Components of known path (value or mod name)
|
// Components of known path (value or mod name)
|
||||||
std,
|
std,
|
||||||
core,
|
core,
|
||||||
|
|
|
||||||
|
|
@ -133,6 +133,10 @@ pub(crate) mod fragments {
|
||||||
|
|
||||||
m.complete(p, MACRO_STMTS);
|
m.complete(p, MACRO_STMTS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn attr(p: &mut Parser) {
|
||||||
|
attributes::outer_attrs(p)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn reparser(
|
pub(crate) fn reparser(
|
||||||
|
|
|
||||||
|
|
@ -99,6 +99,8 @@ pub enum FragmentKind {
|
||||||
// FIXME: use separate fragment kinds for macro inputs and outputs?
|
// FIXME: use separate fragment kinds for macro inputs and outputs?
|
||||||
Items,
|
Items,
|
||||||
Statements,
|
Statements,
|
||||||
|
|
||||||
|
Attr,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_fragment(
|
pub fn parse_fragment(
|
||||||
|
|
@ -118,6 +120,7 @@ pub fn parse_fragment(
|
||||||
FragmentKind::Statement => grammar::fragments::stmt,
|
FragmentKind::Statement => grammar::fragments::stmt,
|
||||||
FragmentKind::Items => grammar::fragments::macro_items,
|
FragmentKind::Items => grammar::fragments::macro_items,
|
||||||
FragmentKind::Statements => grammar::fragments::macro_stmts,
|
FragmentKind::Statements => grammar::fragments::macro_stmts,
|
||||||
|
FragmentKind::Attr => grammar::fragments::attr,
|
||||||
};
|
};
|
||||||
parse_from_tokens(token_source, tree_sink, parser)
|
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.
|
/// Matches a `SyntaxNode` against an `ast` type.
|
||||||
///
|
///
|
||||||
/// # Example:
|
/// # Example:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue