diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs index 9cd0b72aa9..b8d9c26822 100644 --- a/crates/hir_def/src/attr.rs +++ b/crates/hir_def/src/attr.rs @@ -122,9 +122,67 @@ 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 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 { + let attr = Subtree { delimiter: None, token_trees: attr }; + let attr = ast::Attr::parse(&attr.to_string()).ok()?; + let hygiene = Hygiene::new_unhygienic(); // FIXME + Attr::from_src(attr, &hygiene) + } + }) + .collect(); + + Attrs(RawAttrs { entries: Some(new_attrs) }) } } diff --git a/crates/hir_expand/src/name.rs b/crates/hir_expand/src/name.rs index 7fb4caea36..77eeee3fe9 100644 --- a/crates/hir_expand/src/name.rs +++ b/crates/hir_expand/src/name.rs @@ -153,6 +153,7 @@ pub mod known { // Special names macro_rules, doc, + cfg_attr, // Components of known path (value or mod name) std, core, diff --git a/crates/parser/src/grammar.rs b/crates/parser/src/grammar.rs index 23039eba49..1a078f6b4c 100644 --- a/crates/parser/src/grammar.rs +++ b/crates/parser/src/grammar.rs @@ -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( diff --git a/crates/parser/src/lib.rs b/crates/parser/src/lib.rs index 41e62116f8..ab8e4c70e8 100644 --- a/crates/parser/src/lib.rs +++ b/crates/parser/src/lib.rs @@ -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) } diff --git a/crates/syntax/src/lib.rs b/crates/syntax/src/lib.rs index e753b11bb8..4d272f367a 100644 --- a/crates/syntax/src/lib.rs +++ b/crates/syntax/src/lib.rs @@ -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 { + parsing::parse_text_fragment(text, parser::FragmentKind::Attr) + } +} + /// Matches a `SyntaxNode` against an `ast` type. /// /// # Example: