about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2020-12-18 19:27:18 +0000
committerGitHub <noreply@github.com>2020-12-18 19:27:18 +0000
commitc073e4f6ba5e4f2754262ea02ad3bb23e78e1f3e (patch)
tree524b2882b071232a2603f657279e7ac9c459950f
parent25185c1418022868e2f7ec1599e32a34d63e8314 (diff)
parentaab9cc9cfb64aea659f2f9b588e8a4f392e2c4e1 (diff)
downloadrust-c073e4f6ba5e4f2754262ea02ad3bb23e78e1f3e.tar.gz
rust-c073e4f6ba5e4f2754262ea02ad3bb23e78e1f3e.zip
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>
-rw-r--r--crates/hir_def/src/attr.rs67
-rw-r--r--crates/hir_def/src/nameres/tests/diagnostics.rs18
-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
6 files changed, 97 insertions, 3 deletions
diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs
index 9cd0b72aa98..1b9c64ee55a 100644
--- a/crates/hir_def/src/attr.rs
+++ b/crates/hir_def/src/attr.rs
@@ -12,6 +12,7 @@ use syntax::{
     ast::{self, AstNode, AttrsOwner},
     match_ast, AstToken, SmolStr, SyntaxNode,
 };
+use test_utils::mark;
 use tt::Subtree;
 
 use crate::{
@@ -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
+                    Attr::from_src(attr, &hygiene)
+                }
+            })
+            .collect();
+
+        Attrs(RawAttrs { entries: Some(new_attrs) })
     }
 }
 
diff --git a/crates/hir_def/src/nameres/tests/diagnostics.rs b/crates/hir_def/src/nameres/tests/diagnostics.rs
index 1a7b988318e..58d69d3c6b2 100644
--- a/crates/hir_def/src/nameres/tests/diagnostics.rs
+++ b/crates/hir_def/src/nameres/tests/diagnostics.rs
@@ -1,4 +1,5 @@
 use base_db::fixture::WithFixture;
+use test_utils::mark;
 
 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() {}
+        "#,
+    );
+}
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: