From 08de1b4fa57ca78ad13026950b3eb024b7d2abf3 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Fri, 18 Dec 2020 18:58:42 +0100 Subject: Implement `RawAttr::filter` --- crates/hir_def/src/attr.rs | 64 +++++++++++++++++++++++++++++++++++++++++-- crates/hir_expand/src/name.rs | 1 + crates/parser/src/grammar.rs | 4 +++ crates/parser/src/lib.rs | 3 ++ crates/syntax/src/lib.rs | 7 +++++ 5 files changed, 76 insertions(+), 3 deletions(-) (limited to 'crates') diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs index 9cd0b72aa..b8d9c2682 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 7fb4caea3..77eeee3fe 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 23039eba4..1a078f6b4 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 41e62116f..ab8e4c70e 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 e753b11bb..4d272f367 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: -- cgit v1.2.3 From d42d1d33f029ac09d4848c102b53ca39da6d0f4e Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Fri, 18 Dec 2020 20:00:59 +0100 Subject: Fix parsing of active cfg_attr --- crates/hir_def/src/attr.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'crates') diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs index b8d9c2682..68d05c54d 100644 --- a/crates/hir_def/src/attr.rs +++ b/crates/hir_def/src/attr.rs @@ -167,6 +167,7 @@ impl RawAttrs { }); 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); @@ -174,8 +175,7 @@ impl RawAttrs { 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 attr = ast::Attr::parse(&format!("#[{}]", attr)).ok()?; let hygiene = Hygiene::new_unhygienic(); // FIXME Attr::from_src(attr, &hygiene) } -- cgit v1.2.3 From 382ee2fa037a3ec293d332dd70f04975396b5513 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Fri, 18 Dec 2020 20:01:02 +0100 Subject: Add test --- crates/hir_def/src/nameres/tests/diagnostics.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'crates') diff --git a/crates/hir_def/src/nameres/tests/diagnostics.rs b/crates/hir_def/src/nameres/tests/diagnostics.rs index 1a7b98831..4093f8bd0 100644 --- a/crates/hir_def/src/nameres/tests/diagnostics.rs +++ b/crates/hir_def/src/nameres/tests/diagnostics.rs @@ -119,3 +119,19 @@ fn inactive_item() { "#, ); } + +/// Tests that `cfg` attributes behind `cfg_attr` is handled properly. +#[test] +fn inactive_via_cfg_attr() { + 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() {} + "#, + ); +} -- cgit v1.2.3 From aab9cc9cfb64aea659f2f9b588e8a4f392e2c4e1 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Fri, 18 Dec 2020 20:25:41 +0100 Subject: Hit a mark --- crates/hir_def/src/attr.rs | 3 +++ crates/hir_def/src/nameres/tests/diagnostics.rs | 2 ++ 2 files changed, 5 insertions(+) (limited to 'crates') diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs index 68d05c54d..1b9c64ee5 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::{ @@ -175,6 +176,8 @@ impl RawAttrs { 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) diff --git a/crates/hir_def/src/nameres/tests/diagnostics.rs b/crates/hir_def/src/nameres/tests/diagnostics.rs index 4093f8bd0..58d69d3c6 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; @@ -123,6 +124,7 @@ 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 -- cgit v1.2.3