diff options
-rw-r--r-- | crates/hir_def/src/attr.rs | 67 | ||||
-rw-r--r-- | crates/hir_def/src/nameres/tests/diagnostics.rs | 18 | ||||
-rw-r--r-- | crates/hir_expand/src/name.rs | 1 | ||||
-rw-r--r-- | crates/parser/src/grammar.rs | 4 | ||||
-rw-r--r-- | crates/parser/src/lib.rs | 3 | ||||
-rw-r--r-- | crates/syntax/src/lib.rs | 7 |
6 files changed, 97 insertions, 3 deletions
diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs index 9cd0b72aa..1b9c64ee5 100644 --- a/crates/hir_def/src/attr.rs +++ b/crates/hir_def/src/attr.rs | |||
@@ -12,6 +12,7 @@ use syntax::{ | |||
12 | ast::{self, AstNode, AttrsOwner}, | 12 | ast::{self, AstNode, AttrsOwner}, |
13 | match_ast, AstToken, SmolStr, SyntaxNode, | 13 | match_ast, AstToken, SmolStr, SyntaxNode, |
14 | }; | 14 | }; |
15 | use test_utils::mark; | ||
15 | use tt::Subtree; | 16 | use tt::Subtree; |
16 | 17 | ||
17 | use crate::{ | 18 | use crate::{ |
@@ -122,9 +123,69 @@ impl RawAttrs { | |||
122 | } | 123 | } |
123 | 124 | ||
124 | /// Processes `cfg_attr`s, returning the resulting semantic `Attrs`. | 125 | /// Processes `cfg_attr`s, returning the resulting semantic `Attrs`. |
125 | pub(crate) fn filter(self, _db: &dyn DefDatabase, _krate: CrateId) -> Attrs { | 126 | pub(crate) fn filter(self, db: &dyn DefDatabase, krate: CrateId) -> Attrs { |
126 | // FIXME actually implement this | 127 | let has_cfg_attrs = self.iter().any(|attr| { |
127 | Attrs(self) | 128 | attr.path.as_ident().map_or(false, |name| *name == hir_expand::name![cfg_attr]) |
129 | }); | ||
130 | if !has_cfg_attrs { | ||
131 | return Attrs(self); | ||
132 | } | ||
133 | |||
134 | let crate_graph = db.crate_graph(); | ||
135 | let new_attrs = self | ||
136 | .iter() | ||
137 | .filter_map(|attr| { | ||
138 | let attr = attr.clone(); | ||
139 | let is_cfg_attr = | ||
140 | attr.path.as_ident().map_or(false, |name| *name == hir_expand::name![cfg_attr]); | ||
141 | if !is_cfg_attr { | ||
142 | return Some(attr); | ||
143 | } | ||
144 | |||
145 | let subtree = match &attr.input { | ||
146 | Some(AttrInput::TokenTree(it)) => it, | ||
147 | _ => return Some(attr), | ||
148 | }; | ||
149 | |||
150 | // Input subtree is: `(cfg, attr)` | ||
151 | // Split it up into a `cfg` and an `attr` subtree. | ||
152 | // FIXME: There should be a common API for this. | ||
153 | let mut saw_comma = false; | ||
154 | let (mut cfg, attr): (Vec<_>, Vec<_>) = | ||
155 | subtree.clone().token_trees.into_iter().partition(|tree| { | ||
156 | if saw_comma { | ||
157 | return false; | ||
158 | } | ||
159 | |||
160 | match tree { | ||
161 | tt::TokenTree::Leaf(tt::Leaf::Punct(p)) if p.char == ',' => { | ||
162 | saw_comma = true; | ||
163 | } | ||
164 | _ => {} | ||
165 | } | ||
166 | |||
167 | true | ||
168 | }); | ||
169 | cfg.pop(); // `,` ends up in here | ||
170 | |||
171 | let attr = Subtree { delimiter: None, token_trees: attr }; | ||
172 | let cfg = Subtree { delimiter: subtree.delimiter, token_trees: cfg }; | ||
173 | let cfg = CfgExpr::parse(&cfg); | ||
174 | |||
175 | let cfg_options = &crate_graph[krate].cfg_options; | ||
176 | if cfg_options.check(&cfg) == Some(false) { | ||
177 | None | ||
178 | } else { | ||
179 | mark::hit!(cfg_attr_active); | ||
180 | |||
181 | let attr = ast::Attr::parse(&format!("#[{}]", attr)).ok()?; | ||
182 | let hygiene = Hygiene::new_unhygienic(); // FIXME | ||
183 | Attr::from_src(attr, &hygiene) | ||
184 | } | ||
185 | }) | ||
186 | .collect(); | ||
187 | |||
188 | Attrs(RawAttrs { entries: Some(new_attrs) }) | ||
128 | } | 189 | } |
129 | } | 190 | } |
130 | 191 | ||
diff --git a/crates/hir_def/src/nameres/tests/diagnostics.rs b/crates/hir_def/src/nameres/tests/diagnostics.rs index 1a7b98831..58d69d3c6 100644 --- a/crates/hir_def/src/nameres/tests/diagnostics.rs +++ b/crates/hir_def/src/nameres/tests/diagnostics.rs | |||
@@ -1,4 +1,5 @@ | |||
1 | use base_db::fixture::WithFixture; | 1 | use base_db::fixture::WithFixture; |
2 | use test_utils::mark; | ||
2 | 3 | ||
3 | use crate::test_db::TestDB; | 4 | use crate::test_db::TestDB; |
4 | 5 | ||
@@ -119,3 +120,20 @@ fn inactive_item() { | |||
119 | "#, | 120 | "#, |
120 | ); | 121 | ); |
121 | } | 122 | } |
123 | |||
124 | /// Tests that `cfg` attributes behind `cfg_attr` is handled properly. | ||
125 | #[test] | ||
126 | fn inactive_via_cfg_attr() { | ||
127 | mark::check!(cfg_attr_active); | ||
128 | check_diagnostics( | ||
129 | r#" | ||
130 | //- /lib.rs | ||
131 | #[cfg_attr(not(never), cfg(no))] fn f() {} | ||
132 | //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: no is disabled | ||
133 | |||
134 | #[cfg_attr(not(never), cfg(not(no)))] fn f() {} | ||
135 | |||
136 | #[cfg_attr(never, cfg(no))] fn g() {} | ||
137 | "#, | ||
138 | ); | ||
139 | } | ||
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 { | |||
153 | // Special names | 153 | // Special names |
154 | macro_rules, | 154 | macro_rules, |
155 | doc, | 155 | doc, |
156 | cfg_attr, | ||
156 | // Components of known path (value or mod name) | 157 | // Components of known path (value or mod name) |
157 | std, | 158 | std, |
158 | core, | 159 | 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 { | |||
133 | 133 | ||
134 | m.complete(p, MACRO_STMTS); | 134 | m.complete(p, MACRO_STMTS); |
135 | } | 135 | } |
136 | |||
137 | pub(crate) fn attr(p: &mut Parser) { | ||
138 | attributes::outer_attrs(p) | ||
139 | } | ||
136 | } | 140 | } |
137 | 141 | ||
138 | pub(crate) fn reparser( | 142 | 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 { | |||
99 | // FIXME: use separate fragment kinds for macro inputs and outputs? | 99 | // FIXME: use separate fragment kinds for macro inputs and outputs? |
100 | Items, | 100 | Items, |
101 | Statements, | 101 | Statements, |
102 | |||
103 | Attr, | ||
102 | } | 104 | } |
103 | 105 | ||
104 | pub fn parse_fragment( | 106 | pub fn parse_fragment( |
@@ -118,6 +120,7 @@ pub fn parse_fragment( | |||
118 | FragmentKind::Statement => grammar::fragments::stmt, | 120 | FragmentKind::Statement => grammar::fragments::stmt, |
119 | FragmentKind::Items => grammar::fragments::macro_items, | 121 | FragmentKind::Items => grammar::fragments::macro_items, |
120 | FragmentKind::Statements => grammar::fragments::macro_stmts, | 122 | FragmentKind::Statements => grammar::fragments::macro_stmts, |
123 | FragmentKind::Attr => grammar::fragments::attr, | ||
121 | }; | 124 | }; |
122 | parse_from_tokens(token_source, tree_sink, parser) | 125 | parse_from_tokens(token_source, tree_sink, parser) |
123 | } | 126 | } |
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 { | |||
205 | } | 205 | } |
206 | } | 206 | } |
207 | 207 | ||
208 | impl ast::Attr { | ||
209 | /// Returns `text`, parsed as an attribute, but only if it has no errors. | ||
210 | pub fn parse(text: &str) -> Result<Self, ()> { | ||
211 | parsing::parse_text_fragment(text, parser::FragmentKind::Attr) | ||
212 | } | ||
213 | } | ||
214 | |||
208 | /// Matches a `SyntaxNode` against an `ast` type. | 215 | /// Matches a `SyntaxNode` against an `ast` type. |
209 | /// | 216 | /// |
210 | /// # Example: | 217 | /// # Example: |