aboutsummaryrefslogtreecommitdiff
path: root/crates/hir_def/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/hir_def/src')
-rw-r--r--crates/hir_def/src/nameres/collector.rs119
-rw-r--r--crates/hir_def/src/nameres/tests/diagnostics.rs17
2 files changed, 98 insertions, 38 deletions
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs
index dfed729df..e76d039b8 100644
--- a/crates/hir_def/src/nameres/collector.rs
+++ b/crates/hir_def/src/nameres/collector.rs
@@ -20,7 +20,7 @@ use rustc_hash::{FxHashMap, FxHashSet};
20use syntax::ast; 20use syntax::ast;
21 21
22use crate::{ 22use crate::{
23 attr::{Attr, AttrId, Attrs}, 23 attr::{Attr, AttrId, AttrInput, Attrs},
24 builtin_attr, 24 builtin_attr,
25 db::DefDatabase, 25 db::DefDatabase,
26 derive_macro_as_call_id, 26 derive_macro_as_call_id,
@@ -100,8 +100,10 @@ pub(super) fn collect_defs(
100 proc_macros, 100 proc_macros,
101 exports_proc_macros: false, 101 exports_proc_macros: false,
102 from_glob_import: Default::default(), 102 from_glob_import: Default::default(),
103 ignore_attrs_on: Default::default(), 103 skip_attrs: Default::default(),
104 derive_helpers_in_scope: Default::default(), 104 derive_helpers_in_scope: Default::default(),
105 registered_attrs: Default::default(),
106 registered_tools: Default::default(),
105 }; 107 };
106 match block { 108 match block {
107 Some(block) => { 109 Some(block) => {
@@ -253,10 +255,14 @@ struct DefCollector<'a> {
253 /// 255 ///
254 /// This also stores the attributes to skip when we resolve derive helpers and non-macro 256 /// This also stores the attributes to skip when we resolve derive helpers and non-macro
255 /// non-builtin attributes in general. 257 /// non-builtin attributes in general.
256 ignore_attrs_on: FxHashMap<InFile<ModItem>, AttrId>, 258 skip_attrs: FxHashMap<InFile<ModItem>, AttrId>,
257 /// Tracks which custom derives are in scope for an item, to allow resolution of derive helper 259 /// Tracks which custom derives are in scope for an item, to allow resolution of derive helper
258 /// attributes. 260 /// attributes.
259 derive_helpers_in_scope: FxHashMap<AstId<ast::Item>, Vec<Name>>, 261 derive_helpers_in_scope: FxHashMap<AstId<ast::Item>, Vec<Name>>,
262 /// Custom attributes registered with `#![register_attr]`.
263 registered_attrs: Vec<String>,
264 /// Custom tool modules registered with `#![register_tool]`.
265 registered_tools: Vec<String>,
260} 266}
261 267
262impl DefCollector<'_> { 268impl DefCollector<'_> {
@@ -265,11 +271,39 @@ impl DefCollector<'_> {
265 let item_tree = self.db.file_item_tree(file_id.into()); 271 let item_tree = self.db.file_item_tree(file_id.into());
266 let module_id = self.def_map.root; 272 let module_id = self.def_map.root;
267 self.def_map.modules[module_id].origin = ModuleOrigin::CrateRoot { definition: file_id }; 273 self.def_map.modules[module_id].origin = ModuleOrigin::CrateRoot { definition: file_id };
268 if item_tree 274
269 .top_level_attrs(self.db, self.def_map.krate) 275 let attrs = item_tree.top_level_attrs(self.db, self.def_map.krate);
270 .cfg() 276 if attrs.cfg().map_or(true, |cfg| self.cfg_options.check(&cfg) != Some(false)) {
271 .map_or(true, |cfg| self.cfg_options.check(&cfg) != Some(false)) 277 // Process other crate-level attributes.
272 { 278 for attr in &*attrs {
279 let attr_name = match attr.path.as_ident() {
280 Some(name) => name,
281 None => continue,
282 };
283
284 let registered_name = if *attr_name == hir_expand::name![register_attr]
285 || *attr_name == hir_expand::name![register_tool]
286 {
287 match &attr.input {
288 Some(AttrInput::TokenTree(subtree)) => match &*subtree.token_trees {
289 [tt::TokenTree::Leaf(tt::Leaf::Ident(name))] => name.as_name(),
290 _ => continue,
291 },
292 _ => continue,
293 }
294 } else {
295 continue;
296 };
297
298 if *attr_name == hir_expand::name![register_attr] {
299 self.registered_attrs.push(registered_name.to_string());
300 cov_mark::hit!(register_attr);
301 } else {
302 self.registered_tools.push(registered_name.to_string());
303 cov_mark::hit!(register_tool);
304 }
305 }
306
273 ModCollector { 307 ModCollector {
274 def_collector: &mut *self, 308 def_collector: &mut *self,
275 macro_depth: 0, 309 macro_depth: 0,
@@ -382,7 +416,7 @@ impl DefCollector<'_> {
382 let mut unresolved_macros = std::mem::replace(&mut self.unresolved_macros, Vec::new()); 416 let mut unresolved_macros = std::mem::replace(&mut self.unresolved_macros, Vec::new());
383 let pos = unresolved_macros.iter().position(|directive| { 417 let pos = unresolved_macros.iter().position(|directive| {
384 if let MacroDirectiveKind::Attr { ast_id, mod_item, attr } = &directive.kind { 418 if let MacroDirectiveKind::Attr { ast_id, mod_item, attr } = &directive.kind {
385 self.ignore_attrs_on.insert(ast_id.ast_id.with_value(*mod_item), *attr); 419 self.skip_attrs.insert(ast_id.ast_id.with_value(*mod_item), *attr);
386 420
387 let file_id = ast_id.ast_id.file_id; 421 let file_id = ast_id.ast_id.file_id;
388 let item_tree = self.db.file_item_tree(file_id); 422 let item_tree = self.db.file_item_tree(file_id);
@@ -941,7 +975,7 @@ impl DefCollector<'_> {
941 let file_id = ast_id.ast_id.file_id; 975 let file_id = ast_id.ast_id.file_id;
942 let item_tree = self.db.file_item_tree(file_id); 976 let item_tree = self.db.file_item_tree(file_id);
943 let mod_dir = self.mod_dirs[&directive.module_id].clone(); 977 let mod_dir = self.mod_dirs[&directive.module_id].clone();
944 self.ignore_attrs_on.insert(InFile::new(file_id, *mod_item), *attr); 978 self.skip_attrs.insert(InFile::new(file_id, *mod_item), *attr);
945 ModCollector { 979 ModCollector {
946 def_collector: &mut *self, 980 def_collector: &mut *self,
947 macro_depth: directive.depth, 981 macro_depth: directive.depth,
@@ -1479,32 +1513,8 @@ impl ModCollector<'_, '_> {
1479 /// If `ignore_up_to` is `Some`, attributes precending and including that attribute will be 1513 /// If `ignore_up_to` is `Some`, attributes precending and including that attribute will be
1480 /// assumed to be resolved already. 1514 /// assumed to be resolved already.
1481 fn resolve_attributes(&mut self, attrs: &Attrs, mod_item: ModItem) -> Result<(), ()> { 1515 fn resolve_attributes(&mut self, attrs: &Attrs, mod_item: ModItem) -> Result<(), ()> {
1482 fn is_builtin_attr(path: &ModPath) -> bool {
1483 if path.kind == PathKind::Plain {
1484 if let Some(tool_module) = path.segments().first() {
1485 let tool_module = tool_module.to_string();
1486 if builtin_attr::TOOL_MODULES.iter().any(|m| tool_module == *m) {
1487 return true;
1488 }
1489 }
1490
1491 if let Some(name) = path.as_ident() {
1492 let name = name.to_string();
1493 if builtin_attr::INERT_ATTRIBUTES
1494 .iter()
1495 .chain(builtin_attr::EXTRA_ATTRIBUTES)
1496 .any(|attr| name == *attr)
1497 {
1498 return true;
1499 }
1500 }
1501 }
1502
1503 false
1504 }
1505
1506 let mut ignore_up_to = 1516 let mut ignore_up_to =
1507 self.def_collector.ignore_attrs_on.get(&InFile::new(self.file_id, mod_item)).copied(); 1517 self.def_collector.skip_attrs.get(&InFile::new(self.file_id, mod_item)).copied();
1508 for attr in attrs.iter().skip_while(|attr| match ignore_up_to { 1518 for attr in attrs.iter().skip_while(|attr| match ignore_up_to {
1509 Some(id) if attr.id == id => { 1519 Some(id) if attr.id == id => {
1510 ignore_up_to = None; 1520 ignore_up_to = None;
@@ -1515,7 +1525,7 @@ impl ModCollector<'_, '_> {
1515 }) { 1525 }) {
1516 if attr.path.as_ident() == Some(&hir_expand::name![derive]) { 1526 if attr.path.as_ident() == Some(&hir_expand::name![derive]) {
1517 self.collect_derive(attr, mod_item); 1527 self.collect_derive(attr, mod_item);
1518 } else if is_builtin_attr(&attr.path) { 1528 } else if self.is_builtin_or_registered_attr(&attr.path) {
1519 continue; 1529 continue;
1520 } else { 1530 } else {
1521 log::debug!("non-builtin attribute {}", attr.path); 1531 log::debug!("non-builtin attribute {}", attr.path);
@@ -1538,6 +1548,37 @@ impl ModCollector<'_, '_> {
1538 Ok(()) 1548 Ok(())
1539 } 1549 }
1540 1550
1551 fn is_builtin_or_registered_attr(&self, path: &ModPath) -> bool {
1552 if path.kind == PathKind::Plain {
1553 if let Some(tool_module) = path.segments().first() {
1554 let tool_module = tool_module.to_string();
1555 if builtin_attr::TOOL_MODULES
1556 .iter()
1557 .copied()
1558 .chain(self.def_collector.registered_tools.iter().map(|s| &**s))
1559 .any(|m| tool_module == *m)
1560 {
1561 return true;
1562 }
1563 }
1564
1565 if let Some(name) = path.as_ident() {
1566 let name = name.to_string();
1567 if builtin_attr::INERT_ATTRIBUTES
1568 .iter()
1569 .chain(builtin_attr::EXTRA_ATTRIBUTES)
1570 .copied()
1571 .chain(self.def_collector.registered_attrs.iter().map(|s| &**s))
1572 .any(|attr| name == *attr)
1573 {
1574 return true;
1575 }
1576 }
1577 }
1578
1579 false
1580 }
1581
1541 fn collect_derive(&mut self, attr: &Attr, mod_item: ModItem) { 1582 fn collect_derive(&mut self, attr: &Attr, mod_item: ModItem) {
1542 let ast_id: FileAstId<ast::Item> = match mod_item { 1583 let ast_id: FileAstId<ast::Item> = match mod_item {
1543 ModItem::Struct(it) => self.item_tree[it].ast_id.upcast(), 1584 ModItem::Struct(it) => self.item_tree[it].ast_id.upcast(),
@@ -1779,8 +1820,10 @@ mod tests {
1779 proc_macros: Default::default(), 1820 proc_macros: Default::default(),
1780 exports_proc_macros: false, 1821 exports_proc_macros: false,
1781 from_glob_import: Default::default(), 1822 from_glob_import: Default::default(),
1782 ignore_attrs_on: Default::default(), 1823 skip_attrs: Default::default(),
1783 derive_helpers_in_scope: FxHashMap::default(), 1824 derive_helpers_in_scope: Default::default(),
1825 registered_attrs: Default::default(),
1826 registered_tools: Default::default(),
1784 }; 1827 };
1785 collector.seed_with_top_level(); 1828 collector.seed_with_top_level();
1786 collector.collect(); 1829 collector.collect();
diff --git a/crates/hir_def/src/nameres/tests/diagnostics.rs b/crates/hir_def/src/nameres/tests/diagnostics.rs
index 543975e07..75147d973 100644
--- a/crates/hir_def/src/nameres/tests/diagnostics.rs
+++ b/crates/hir_def/src/nameres/tests/diagnostics.rs
@@ -237,3 +237,20 @@ fn good_out_dir_diagnostic() {
237 "#, 237 "#,
238 ); 238 );
239} 239}
240
241#[test]
242fn register_attr_and_tool() {
243 cov_mark::check!(register_attr);
244 cov_mark::check!(register_tool);
245 check_no_diagnostics(
246 r#"
247#![register_tool(tool)]
248#![register_attr(attr)]
249
250#[tool::path]
251#[attr]
252struct S;
253 "#,
254 );
255 // NB: we don't currently emit diagnostics here
256}