Skip to content

Commit c073e4f

Browse files
Merge #6934
6934: Implement `cfg_attr` handling r=jonas-schievink a=jonas-schievink Part of #5548 Co-authored-by: Jonas Schievink <[email protected]>
2 parents 25185c1 + aab9cc9 commit c073e4f

File tree

6 files changed

+97
-3
lines changed

6 files changed

+97
-3
lines changed

crates/hir_def/src/attr.rs

Lines changed: 64 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use syntax::{
1212
ast::{self, AstNode, AttrsOwner},
1313
match_ast, AstToken, SmolStr, SyntaxNode,
1414
};
15+
use test_utils::mark;
1516
use tt::Subtree;
1617

1718
use crate::{
@@ -122,9 +123,69 @@ impl RawAttrs {
122123
}
123124

124125
/// Processes `cfg_attr`s, returning the resulting semantic `Attrs`.
125-
pub(crate) fn filter(self, _db: &dyn DefDatabase, _krate: CrateId) -> Attrs {
126-
// FIXME actually implement this
127-
Attrs(self)
126+
pub(crate) fn filter(self, db: &dyn DefDatabase, krate: CrateId) -> Attrs {
127+
let has_cfg_attrs = self.iter().any(|attr| {
128+
attr.path.as_ident().map_or(false, |name| *name == hir_expand::name![cfg_attr])
129+
});
130+
if !has_cfg_attrs {
131+
return Attrs(self);
132+
}
133+
134+
let crate_graph = db.crate_graph();
135+
let new_attrs = self
136+
.iter()
137+
.filter_map(|attr| {
138+
let attr = attr.clone();
139+
let is_cfg_attr =
140+
attr.path.as_ident().map_or(false, |name| *name == hir_expand::name![cfg_attr]);
141+
if !is_cfg_attr {
142+
return Some(attr);
143+
}
144+
145+
let subtree = match &attr.input {
146+
Some(AttrInput::TokenTree(it)) => it,
147+
_ => return Some(attr),
148+
};
149+
150+
// Input subtree is: `(cfg, attr)`
151+
// Split it up into a `cfg` and an `attr` subtree.
152+
// FIXME: There should be a common API for this.
153+
let mut saw_comma = false;
154+
let (mut cfg, attr): (Vec<_>, Vec<_>) =
155+
subtree.clone().token_trees.into_iter().partition(|tree| {
156+
if saw_comma {
157+
return false;
158+
}
159+
160+
match tree {
161+
tt::TokenTree::Leaf(tt::Leaf::Punct(p)) if p.char == ',' => {
162+
saw_comma = true;
163+
}
164+
_ => {}
165+
}
166+
167+
true
168+
});
169+
cfg.pop(); // `,` ends up in here
170+
171+
let attr = Subtree { delimiter: None, token_trees: attr };
172+
let cfg = Subtree { delimiter: subtree.delimiter, token_trees: cfg };
173+
let cfg = CfgExpr::parse(&cfg);
174+
175+
let cfg_options = &crate_graph[krate].cfg_options;
176+
if cfg_options.check(&cfg) == Some(false) {
177+
None
178+
} else {
179+
mark::hit!(cfg_attr_active);
180+
181+
let attr = ast::Attr::parse(&format!("#[{}]", attr)).ok()?;
182+
let hygiene = Hygiene::new_unhygienic(); // FIXME
183+
Attr::from_src(attr, &hygiene)
184+
}
185+
})
186+
.collect();
187+
188+
Attrs(RawAttrs { entries: Some(new_attrs) })
128189
}
129190
}
130191

crates/hir_def/src/nameres/tests/diagnostics.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use base_db::fixture::WithFixture;
2+
use test_utils::mark;
23

34
use crate::test_db::TestDB;
45

@@ -119,3 +120,20 @@ fn inactive_item() {
119120
"#,
120121
);
121122
}
123+
124+
/// Tests that `cfg` attributes behind `cfg_attr` is handled properly.
125+
#[test]
126+
fn inactive_via_cfg_attr() {
127+
mark::check!(cfg_attr_active);
128+
check_diagnostics(
129+
r#"
130+
//- /lib.rs
131+
#[cfg_attr(not(never), cfg(no))] fn f() {}
132+
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: no is disabled
133+
134+
#[cfg_attr(not(never), cfg(not(no)))] fn f() {}
135+
136+
#[cfg_attr(never, cfg(no))] fn g() {}
137+
"#,
138+
);
139+
}

crates/hir_expand/src/name.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ pub mod known {
153153
// Special names
154154
macro_rules,
155155
doc,
156+
cfg_attr,
156157
// Components of known path (value or mod name)
157158
std,
158159
core,

crates/parser/src/grammar.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,10 @@ pub(crate) mod fragments {
133133

134134
m.complete(p, MACRO_STMTS);
135135
}
136+
137+
pub(crate) fn attr(p: &mut Parser) {
138+
attributes::outer_attrs(p)
139+
}
136140
}
137141

138142
pub(crate) fn reparser(

crates/parser/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,8 @@ pub enum FragmentKind {
9999
// FIXME: use separate fragment kinds for macro inputs and outputs?
100100
Items,
101101
Statements,
102+
103+
Attr,
102104
}
103105

104106
pub fn parse_fragment(
@@ -118,6 +120,7 @@ pub fn parse_fragment(
118120
FragmentKind::Statement => grammar::fragments::stmt,
119121
FragmentKind::Items => grammar::fragments::macro_items,
120122
FragmentKind::Statements => grammar::fragments::macro_stmts,
123+
FragmentKind::Attr => grammar::fragments::attr,
121124
};
122125
parse_from_tokens(token_source, tree_sink, parser)
123126
}

crates/syntax/src/lib.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,13 @@ impl ast::Type {
205205
}
206206
}
207207

208+
impl ast::Attr {
209+
/// Returns `text`, parsed as an attribute, but only if it has no errors.
210+
pub fn parse(text: &str) -> Result<Self, ()> {
211+
parsing::parse_text_fragment(text, parser::FragmentKind::Attr)
212+
}
213+
}
214+
208215
/// Matches a `SyntaxNode` against an `ast` type.
209216
///
210217
/// # Example:

0 commit comments

Comments
 (0)