aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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 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};
15use test_utils::mark;
15use tt::Subtree; 16use tt::Subtree;
16 17
17use crate::{ 18use 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 @@
1use base_db::fixture::WithFixture; 1use base_db::fixture::WithFixture;
2use test_utils::mark;
2 3
3use crate::test_db::TestDB; 4use 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]
126fn 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
138pub(crate) fn reparser( 142pub(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
104pub fn parse_fragment( 106pub 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
208impl 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: