aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/hir_def/src/attr.rs67
-rw-r--r--crates/hir_def/src/item_tree.rs5
-rw-r--r--crates/hir_def/src/item_tree/lower.rs7
-rw-r--r--crates/hir_def/src/nameres/collector.rs31
-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
9 files changed, 125 insertions, 18 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/item_tree.rs b/crates/hir_def/src/item_tree.rs
index 5eb7cae7f..100dbf5d6 100644
--- a/crates/hir_def/src/item_tree.rs
+++ b/crates/hir_def/src/item_tree.rs
@@ -12,7 +12,7 @@ use std::{
12}; 12};
13 13
14use arena::{Arena, Idx, RawId}; 14use arena::{Arena, Idx, RawId};
15use ast::{AstNode, AttrsOwner, NameOwner, StructKind}; 15use ast::{AstNode, NameOwner, StructKind};
16use base_db::CrateId; 16use base_db::CrateId;
17use either::Either; 17use either::Either;
18use hir_expand::{ 18use hir_expand::{
@@ -495,7 +495,6 @@ pub struct Import {
495 pub alias: Option<ImportAlias>, 495 pub alias: Option<ImportAlias>,
496 pub visibility: RawVisibilityId, 496 pub visibility: RawVisibilityId,
497 pub is_glob: bool, 497 pub is_glob: bool,
498 pub is_prelude: bool,
499 /// AST ID of the `use` or `extern crate` item this import was derived from. Note that many 498 /// AST ID of the `use` or `extern crate` item this import was derived from. Note that many
500 /// `Import`s can map to the same `use` item. 499 /// `Import`s can map to the same `use` item.
501 pub ast_id: FileAstId<ast::Use>, 500 pub ast_id: FileAstId<ast::Use>,
@@ -511,8 +510,6 @@ pub struct ExternCrate {
511 pub name: Name, 510 pub name: Name,
512 pub alias: Option<ImportAlias>, 511 pub alias: Option<ImportAlias>,
513 pub visibility: RawVisibilityId, 512 pub visibility: RawVisibilityId,
514 /// Whether this is a `#[macro_use] extern crate ...`.
515 pub is_macro_use: bool,
516 pub ast_id: FileAstId<ast::ExternCrate>, 513 pub ast_id: FileAstId<ast::ExternCrate>,
517} 514}
518 515
diff --git a/crates/hir_def/src/item_tree/lower.rs b/crates/hir_def/src/item_tree/lower.rs
index c8f090c22..3b206ef85 100644
--- a/crates/hir_def/src/item_tree/lower.rs
+++ b/crates/hir_def/src/item_tree/lower.rs
@@ -485,8 +485,6 @@ impl Ctx {
485 } 485 }
486 486
487 fn lower_use(&mut self, use_item: &ast::Use) -> Vec<FileItemTreeId<Import>> { 487 fn lower_use(&mut self, use_item: &ast::Use) -> Vec<FileItemTreeId<Import>> {
488 // FIXME: cfg_attr
489 let is_prelude = use_item.has_atom_attr("prelude_import");
490 let visibility = self.lower_visibility(use_item); 488 let visibility = self.lower_visibility(use_item);
491 let ast_id = self.source_ast_id_map.ast_id(use_item); 489 let ast_id = self.source_ast_id_map.ast_id(use_item);
492 490
@@ -502,7 +500,6 @@ impl Ctx {
502 alias, 500 alias,
503 visibility, 501 visibility,
504 is_glob, 502 is_glob,
505 is_prelude,
506 ast_id, 503 ast_id,
507 index: imports.len(), 504 index: imports.len(),
508 }))); 505 })));
@@ -522,10 +519,8 @@ impl Ctx {
522 }); 519 });
523 let visibility = self.lower_visibility(extern_crate); 520 let visibility = self.lower_visibility(extern_crate);
524 let ast_id = self.source_ast_id_map.ast_id(extern_crate); 521 let ast_id = self.source_ast_id_map.ast_id(extern_crate);
525 // FIXME: cfg_attr
526 let is_macro_use = extern_crate.has_atom_attr("macro_use");
527 522
528 let res = ExternCrate { name, alias, visibility, is_macro_use, ast_id }; 523 let res = ExternCrate { name, alias, visibility, ast_id };
529 Some(id(self.data().extern_crates.alloc(res))) 524 Some(id(self.data().extern_crates.alloc(res)))
530 } 525 }
531 526
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs
index b114a6fe4..55228e480 100644
--- a/crates/hir_def/src/nameres/collector.rs
+++ b/crates/hir_def/src/nameres/collector.rs
@@ -136,23 +136,35 @@ struct Import {
136} 136}
137 137
138impl Import { 138impl Import {
139 fn from_use(tree: &ItemTree, id: ItemTreeId<item_tree::Import>) -> Self { 139 fn from_use(
140 db: &dyn DefDatabase,
141 krate: CrateId,
142 tree: &ItemTree,
143 id: ItemTreeId<item_tree::Import>,
144 ) -> Self {
140 let it = &tree[id.value]; 145 let it = &tree[id.value];
146 let attrs = &tree.attrs(db, krate, ModItem::from(id.value).into());
141 let visibility = &tree[it.visibility]; 147 let visibility = &tree[it.visibility];
142 Self { 148 Self {
143 path: it.path.clone(), 149 path: it.path.clone(),
144 alias: it.alias.clone(), 150 alias: it.alias.clone(),
145 visibility: visibility.clone(), 151 visibility: visibility.clone(),
146 is_glob: it.is_glob, 152 is_glob: it.is_glob,
147 is_prelude: it.is_prelude, 153 is_prelude: attrs.by_key("prelude_import").exists(),
148 is_extern_crate: false, 154 is_extern_crate: false,
149 is_macro_use: false, 155 is_macro_use: false,
150 source: ImportSource::Import(id), 156 source: ImportSource::Import(id),
151 } 157 }
152 } 158 }
153 159
154 fn from_extern_crate(tree: &ItemTree, id: ItemTreeId<item_tree::ExternCrate>) -> Self { 160 fn from_extern_crate(
161 db: &dyn DefDatabase,
162 krate: CrateId,
163 tree: &ItemTree,
164 id: ItemTreeId<item_tree::ExternCrate>,
165 ) -> Self {
155 let it = &tree[id.value]; 166 let it = &tree[id.value];
167 let attrs = &tree.attrs(db, krate, ModItem::from(id.value).into());
156 let visibility = &tree[it.visibility]; 168 let visibility = &tree[it.visibility];
157 Self { 169 Self {
158 path: ModPath::from_segments(PathKind::Plain, iter::once(it.name.clone())), 170 path: ModPath::from_segments(PathKind::Plain, iter::once(it.name.clone())),
@@ -161,7 +173,7 @@ impl Import {
161 is_glob: false, 173 is_glob: false,
162 is_prelude: false, 174 is_prelude: false,
163 is_extern_crate: true, 175 is_extern_crate: true,
164 is_macro_use: it.is_macro_use, 176 is_macro_use: attrs.by_key("macro_use").exists(),
165 source: ImportSource::ExternCrate(id), 177 source: ImportSource::ExternCrate(id),
166 } 178 }
167 } 179 }
@@ -930,7 +942,12 @@ impl ModCollector<'_, '_> {
930 if attrs.cfg().map_or(true, |cfg| self.is_cfg_enabled(&cfg)) { 942 if attrs.cfg().map_or(true, |cfg| self.is_cfg_enabled(&cfg)) {
931 if let ModItem::ExternCrate(id) = item { 943 if let ModItem::ExternCrate(id) = item {
932 let import = self.item_tree[*id].clone(); 944 let import = self.item_tree[*id].clone();
933 if import.is_macro_use { 945 let attrs = self.item_tree.attrs(
946 self.def_collector.db,
947 krate,
948 ModItem::from(*id).into(),
949 );
950 if attrs.by_key("macro_use").exists() {
934 self.def_collector.import_macros_from_extern_crate(self.module_id, &import); 951 self.def_collector.import_macros_from_extern_crate(self.module_id, &import);
935 } 952 }
936 } 953 }
@@ -956,6 +973,8 @@ impl ModCollector<'_, '_> {
956 self.def_collector.unresolved_imports.push(ImportDirective { 973 self.def_collector.unresolved_imports.push(ImportDirective {
957 module_id: self.module_id, 974 module_id: self.module_id,
958 import: Import::from_use( 975 import: Import::from_use(
976 self.def_collector.db,
977 krate,
959 &self.item_tree, 978 &self.item_tree,
960 InFile::new(self.file_id, import_id), 979 InFile::new(self.file_id, import_id),
961 ), 980 ),
@@ -966,6 +985,8 @@ impl ModCollector<'_, '_> {
966 self.def_collector.unresolved_imports.push(ImportDirective { 985 self.def_collector.unresolved_imports.push(ImportDirective {
967 module_id: self.module_id, 986 module_id: self.module_id,
968 import: Import::from_extern_crate( 987 import: Import::from_extern_crate(
988 self.def_collector.db,
989 krate,
969 &self.item_tree, 990 &self.item_tree,
970 InFile::new(self.file_id, import_id), 991 InFile::new(self.file_id, import_id),
971 ), 992 ),
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: