diff options
Diffstat (limited to 'crates/hir_def/src')
-rw-r--r-- | crates/hir_def/src/nameres/collector.rs | 119 | ||||
-rw-r--r-- | crates/hir_def/src/nameres/tests/diagnostics.rs | 17 |
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}; | |||
20 | use syntax::ast; | 20 | use syntax::ast; |
21 | 21 | ||
22 | use crate::{ | 22 | use 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 | ||
262 | impl DefCollector<'_> { | 268 | impl 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] | ||
242 | fn 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] | ||
252 | struct S; | ||
253 | "#, | ||
254 | ); | ||
255 | // NB: we don't currently emit diagnostics here | ||
256 | } | ||