diff options
Diffstat (limited to 'crates/hir_def/src')
-rw-r--r-- | crates/hir_def/src/attr.rs | 67 | ||||
-rw-r--r-- | crates/hir_def/src/item_tree.rs | 5 | ||||
-rw-r--r-- | crates/hir_def/src/item_tree/lower.rs | 7 | ||||
-rw-r--r-- | crates/hir_def/src/nameres/collector.rs | 31 | ||||
-rw-r--r-- | crates/hir_def/src/nameres/tests/diagnostics.rs | 18 |
5 files changed, 110 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 | }; |
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/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 | ||
14 | use arena::{Arena, Idx, RawId}; | 14 | use arena::{Arena, Idx, RawId}; |
15 | use ast::{AstNode, AttrsOwner, NameOwner, StructKind}; | 15 | use ast::{AstNode, NameOwner, StructKind}; |
16 | use base_db::CrateId; | 16 | use base_db::CrateId; |
17 | use either::Either; | 17 | use either::Either; |
18 | use hir_expand::{ | 18 | use 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 | ||
138 | impl Import { | 138 | impl 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 @@ | |||
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 | } | ||