Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 64 additions & 3 deletions crates/hir_def/src/attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use syntax::{
ast::{self, AstNode, AttrsOwner},
match_ast, AstToken, SmolStr, SyntaxNode,
};
use test_utils::mark;
use tt::Subtree;

use crate::{
Expand Down Expand Up @@ -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
Comment on lines +181 to +182
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, this is probably good enough for now :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, I tried doing this properly, but threading Hygiene/HirFileId through everything is a lot of work

Attr::from_src(attr, &hygiene)
}
})
.collect();

Attrs(RawAttrs { entries: Some(new_attrs) })
}
}

Expand Down
18 changes: 18 additions & 0 deletions crates/hir_def/src/nameres/tests/diagnostics.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use base_db::fixture::WithFixture;
use test_utils::mark;

use crate::test_db::TestDB;

Expand Down Expand Up @@ -119,3 +120,20 @@ fn inactive_item() {
"#,
);
}

/// Tests that `cfg` attributes behind `cfg_attr` is handled properly.
#[test]
fn inactive_via_cfg_attr() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this could use a mark

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() {}
"#,
);
}
1 change: 1 addition & 0 deletions crates/hir_expand/src/name.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ pub mod known {
// Special names
macro_rules,
doc,
cfg_attr,
// Components of known path (value or mod name)
std,
core,
Expand Down
4 changes: 4 additions & 0 deletions crates/parser/src/grammar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
3 changes: 3 additions & 0 deletions crates/parser/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ pub enum FragmentKind {
// FIXME: use separate fragment kinds for macro inputs and outputs?
Items,
Statements,

Attr,
}

pub fn parse_fragment(
Expand All @@ -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)
}
Expand Down
7 changes: 7 additions & 0 deletions crates/syntax/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down