about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJonas Schievink <jonasschievink@gmail.com>2020-12-18 18:58:42 +0100
committerJonas Schievink <jonasschievink@gmail.com>2020-12-18 18:58:42 +0100
commit08de1b4fa57ca78ad13026950b3eb024b7d2abf3 (patch)
tree99279afbe385ee36fdcd3921a4a89784439705cf
parent03c177af89efcc238053b59069eda2c1c6772622 (diff)
downloadrust-08de1b4fa57ca78ad13026950b3eb024b7d2abf3.tar.gz
rust-08de1b4fa57ca78ad13026950b3eb024b7d2abf3.zip
Implement `RawAttr::filter`
-rw-r--r--crates/hir_def/src/attr.rs64
-rw-r--r--crates/hir_expand/src/name.rs1
-rw-r--r--crates/parser/src/grammar.rs4
-rw-r--r--crates/parser/src/lib.rs3
-rw-r--r--crates/syntax/src/lib.rs7
5 files changed, 76 insertions, 3 deletions
diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs
index 9cd0b72aa98..b8d9c268222 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 7fb4caea368..77eeee3fe9f 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 23039eba495..1a078f6b4cf 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 41e62116f88..ab8e4c70e80 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 e753b11bb84..4d272f367ad 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<Self, ()> {
+        parsing::parse_text_fragment(text, parser::FragmentKind::Attr)
+    }
+}
+
 /// Matches a `SyntaxNode` against an `ast` type.
 ///
 /// # Example: