aboutsummaryrefslogtreecommitdiff
path: root/crates/hir_def/src/nameres
diff options
context:
space:
mode:
Diffstat (limited to 'crates/hir_def/src/nameres')
-rw-r--r--crates/hir_def/src/nameres/collector.rs314
-rw-r--r--crates/hir_def/src/nameres/tests/diagnostics.rs17
-rw-r--r--crates/hir_def/src/nameres/tests/macros.rs74
3 files changed, 298 insertions, 107 deletions
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs
index 3896be25d..014ea4de4 100644
--- a/crates/hir_def/src/nameres/collector.rs
+++ b/crates/hir_def/src/nameres/collector.rs
@@ -16,19 +16,20 @@ use hir_expand::{
16 FragmentKind, HirFileId, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind, 16 FragmentKind, HirFileId, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind,
17}; 17};
18use hir_expand::{InFile, MacroCallLoc}; 18use hir_expand::{InFile, MacroCallLoc};
19use itertools::Itertools;
19use rustc_hash::{FxHashMap, FxHashSet}; 20use rustc_hash::{FxHashMap, FxHashSet};
20use syntax::ast; 21use syntax::ast;
21 22
22use crate::{ 23use crate::{
23 attr::{AttrId, Attrs}, 24 attr::{Attr, AttrId, AttrInput, Attrs},
24 builtin_attr, 25 builtin_attr,
25 db::DefDatabase, 26 db::DefDatabase,
26 derive_macro_as_call_id, 27 derive_macro_as_call_id,
27 intern::Interned, 28 intern::Interned,
28 item_scope::{ImportType, PerNsGlobImports}, 29 item_scope::{ImportType, PerNsGlobImports},
29 item_tree::{ 30 item_tree::{
30 self, FileItemTreeId, ItemTree, ItemTreeId, MacroCall, MacroDef, MacroRules, Mod, ModItem, 31 self, Fields, FileItemTreeId, ItemTree, ItemTreeId, MacroCall, MacroDef, MacroRules, Mod,
31 ModKind, StructDefKind, 32 ModItem, ModKind,
32 }, 33 },
33 macro_call_as_call_id, 34 macro_call_as_call_id,
34 nameres::{ 35 nameres::{
@@ -94,14 +95,16 @@ pub(super) fn collect_defs(
94 unresolved_imports: Vec::new(), 95 unresolved_imports: Vec::new(),
95 resolved_imports: Vec::new(), 96 resolved_imports: Vec::new(),
96 97
97 unexpanded_macros: Vec::new(), 98 unresolved_macros: Vec::new(),
98 mod_dirs: FxHashMap::default(), 99 mod_dirs: FxHashMap::default(),
99 cfg_options, 100 cfg_options,
100 proc_macros, 101 proc_macros,
101 exports_proc_macros: false, 102 exports_proc_macros: false,
102 from_glob_import: Default::default(), 103 from_glob_import: Default::default(),
103 ignore_attrs_on: FxHashSet::default(), 104 skip_attrs: Default::default(),
104 derive_helpers_in_scope: FxHashMap::default(), 105 derive_helpers_in_scope: Default::default(),
106 registered_attrs: Default::default(),
107 registered_tools: Default::default(),
105 }; 108 };
106 match block { 109 match block {
107 Some(block) => { 110 Some(block) => {
@@ -237,7 +240,7 @@ struct DefCollector<'a> {
237 glob_imports: FxHashMap<LocalModuleId, Vec<(LocalModuleId, Visibility)>>, 240 glob_imports: FxHashMap<LocalModuleId, Vec<(LocalModuleId, Visibility)>>,
238 unresolved_imports: Vec<ImportDirective>, 241 unresolved_imports: Vec<ImportDirective>,
239 resolved_imports: Vec<ImportDirective>, 242 resolved_imports: Vec<ImportDirective>,
240 unexpanded_macros: Vec<MacroDirective>, 243 unresolved_macros: Vec<MacroDirective>,
241 mod_dirs: FxHashMap<LocalModuleId, ModDir>, 244 mod_dirs: FxHashMap<LocalModuleId, ModDir>,
242 cfg_options: &'a CfgOptions, 245 cfg_options: &'a CfgOptions,
243 /// List of procedural macros defined by this crate. This is read from the dynamic library 246 /// List of procedural macros defined by this crate. This is read from the dynamic library
@@ -247,10 +250,20 @@ struct DefCollector<'a> {
247 proc_macros: Vec<(Name, ProcMacroExpander)>, 250 proc_macros: Vec<(Name, ProcMacroExpander)>,
248 exports_proc_macros: bool, 251 exports_proc_macros: bool,
249 from_glob_import: PerNsGlobImports, 252 from_glob_import: PerNsGlobImports,
250 ignore_attrs_on: FxHashSet<InFile<ModItem>>, 253 /// If we fail to resolve an attribute on a `ModItem`, we fall back to ignoring the attribute.
254 /// This map is used to skip all attributes up to and including the one that failed to resolve,
255 /// in order to not expand them twice.
256 ///
257 /// This also stores the attributes to skip when we resolve derive helpers and non-macro
258 /// non-builtin attributes in general.
259 skip_attrs: FxHashMap<InFile<ModItem>, AttrId>,
251 /// Tracks which custom derives are in scope for an item, to allow resolution of derive helper 260 /// Tracks which custom derives are in scope for an item, to allow resolution of derive helper
252 /// attributes. 261 /// attributes.
253 derive_helpers_in_scope: FxHashMap<AstId<ast::Item>, Vec<Name>>, 262 derive_helpers_in_scope: FxHashMap<AstId<ast::Item>, Vec<Name>>,
263 /// Custom attributes registered with `#![register_attr]`.
264 registered_attrs: Vec<String>,
265 /// Custom tool modules registered with `#![register_tool]`.
266 registered_tools: Vec<String>,
254} 267}
255 268
256impl DefCollector<'_> { 269impl DefCollector<'_> {
@@ -259,11 +272,39 @@ impl DefCollector<'_> {
259 let item_tree = self.db.file_item_tree(file_id.into()); 272 let item_tree = self.db.file_item_tree(file_id.into());
260 let module_id = self.def_map.root; 273 let module_id = self.def_map.root;
261 self.def_map.modules[module_id].origin = ModuleOrigin::CrateRoot { definition: file_id }; 274 self.def_map.modules[module_id].origin = ModuleOrigin::CrateRoot { definition: file_id };
262 if item_tree 275
263 .top_level_attrs(self.db, self.def_map.krate) 276 let attrs = item_tree.top_level_attrs(self.db, self.def_map.krate);
264 .cfg() 277 if attrs.cfg().map_or(true, |cfg| self.cfg_options.check(&cfg) != Some(false)) {
265 .map_or(true, |cfg| self.cfg_options.check(&cfg) != Some(false)) 278 // Process other crate-level attributes.
266 { 279 for attr in &*attrs {
280 let attr_name = match attr.path.as_ident() {
281 Some(name) => name,
282 None => continue,
283 };
284
285 let registered_name = if *attr_name == hir_expand::name![register_attr]
286 || *attr_name == hir_expand::name![register_tool]
287 {
288 match &attr.input {
289 Some(AttrInput::TokenTree(subtree)) => match &*subtree.token_trees {
290 [tt::TokenTree::Leaf(tt::Leaf::Ident(name))] => name.as_name(),
291 _ => continue,
292 },
293 _ => continue,
294 }
295 } else {
296 continue;
297 };
298
299 if *attr_name == hir_expand::name![register_attr] {
300 self.registered_attrs.push(registered_name.to_string());
301 cov_mark::hit!(register_attr);
302 } else {
303 self.registered_tools.push(registered_name.to_string());
304 cov_mark::hit!(register_tool);
305 }
306 }
307
267 ModCollector { 308 ModCollector {
268 def_collector: &mut *self, 309 def_collector: &mut *self,
269 macro_depth: 0, 310 macro_depth: 0,
@@ -319,7 +360,7 @@ impl DefCollector<'_> {
319 } 360 }
320 } 361 }
321 362
322 if self.reseed_with_unresolved_attributes() == ReachedFixedPoint::Yes { 363 if self.reseed_with_unresolved_attribute() == ReachedFixedPoint::Yes {
323 break; 364 break;
324 } 365 }
325 } 366 }
@@ -362,27 +403,23 @@ impl DefCollector<'_> {
362 } 403 }
363 404
364 /// When the fixed-point loop reaches a stable state, we might still have some unresolved 405 /// When the fixed-point loop reaches a stable state, we might still have some unresolved
365 /// attributes (or unexpanded attribute proc macros) left over. This takes them, and feeds the 406 /// attributes (or unexpanded attribute proc macros) left over. This takes one of them, and
366 /// item they're applied to back into name resolution. 407 /// feeds the item it's applied to back into name resolution.
367 /// 408 ///
368 /// This effectively ignores the fact that the macro is there and just treats the items as 409 /// This effectively ignores the fact that the macro is there and just treats the items as
369 /// normal code. 410 /// normal code.
370 /// 411 ///
371 /// This improves UX when proc macros are turned off or don't work, and replicates the behavior 412 /// This improves UX when proc macros are turned off or don't work, and replicates the behavior
372 /// before we supported proc. attribute macros. 413 /// before we supported proc. attribute macros.
373 fn reseed_with_unresolved_attributes(&mut self) -> ReachedFixedPoint { 414 fn reseed_with_unresolved_attribute(&mut self) -> ReachedFixedPoint {
374 cov_mark::hit!(unresolved_attribute_fallback); 415 cov_mark::hit!(unresolved_attribute_fallback);
375 416
376 let mut added_items = false; 417 let mut unresolved_macros = std::mem::replace(&mut self.unresolved_macros, Vec::new());
377 let unexpanded_macros = std::mem::replace(&mut self.unexpanded_macros, Vec::new()); 418 let pos = unresolved_macros.iter().position(|directive| {
378 for directive in &unexpanded_macros { 419 if let MacroDirectiveKind::Attr { ast_id, mod_item, attr } = &directive.kind {
379 if let MacroDirectiveKind::Attr { ast_id, mod_item, .. } = &directive.kind { 420 self.skip_attrs.insert(ast_id.ast_id.with_value(*mod_item), *attr);
380 // Make sure to only add such items once.
381 if !self.ignore_attrs_on.insert(ast_id.ast_id.with_value(*mod_item)) {
382 continue;
383 }
384 421
385 let file_id = self.def_map[directive.module_id].definition_source(self.db).file_id; 422 let file_id = ast_id.ast_id.file_id;
386 let item_tree = self.db.file_item_tree(file_id); 423 let item_tree = self.db.file_item_tree(file_id);
387 let mod_dir = self.mod_dirs[&directive.module_id].clone(); 424 let mod_dir = self.mod_dirs[&directive.module_id].clone();
388 ModCollector { 425 ModCollector {
@@ -394,14 +431,20 @@ impl DefCollector<'_> {
394 mod_dir, 431 mod_dir,
395 } 432 }
396 .collect(&[*mod_item]); 433 .collect(&[*mod_item]);
397 added_items = true; 434 true
435 } else {
436 false
398 } 437 }
438 });
439
440 if let Some(pos) = pos {
441 unresolved_macros.remove(pos);
399 } 442 }
400 443
401 // The collection above might add new unresolved macros (eg. derives), so merge the lists. 444 // The collection above might add new unresolved macros (eg. derives), so merge the lists.
402 self.unexpanded_macros.extend(unexpanded_macros); 445 self.unresolved_macros.extend(unresolved_macros);
403 446
404 if added_items { 447 if pos.is_some() {
405 // Continue name resolution with the new data. 448 // Continue name resolution with the new data.
406 ReachedFixedPoint::No 449 ReachedFixedPoint::No
407 } else { 450 } else {
@@ -873,7 +916,7 @@ impl DefCollector<'_> {
873 } 916 }
874 917
875 fn resolve_macros(&mut self) -> ReachedFixedPoint { 918 fn resolve_macros(&mut self) -> ReachedFixedPoint {
876 let mut macros = std::mem::replace(&mut self.unexpanded_macros, Vec::new()); 919 let mut macros = std::mem::replace(&mut self.unresolved_macros, Vec::new());
877 let mut resolved = Vec::new(); 920 let mut resolved = Vec::new();
878 let mut res = ReachedFixedPoint::Yes; 921 let mut res = ReachedFixedPoint::Yes;
879 macros.retain(|directive| { 922 macros.retain(|directive| {
@@ -922,14 +965,43 @@ impl DefCollector<'_> {
922 Err(UnresolvedMacro { .. }) => (), 965 Err(UnresolvedMacro { .. }) => (),
923 } 966 }
924 } 967 }
925 MacroDirectiveKind::Attr { .. } => { 968 MacroDirectiveKind::Attr { ast_id, mod_item, attr } => {
926 // not yet :) 969 if let Some(ident) = ast_id.path.as_ident() {
970 if let Some(helpers) = self.derive_helpers_in_scope.get(&ast_id.ast_id) {
971 if helpers.contains(ident) {
972 cov_mark::hit!(resolved_derive_helper);
973
974 // Resolved to derive helper. Collect the item's attributes again,
975 // starting after the derive helper.
976 let file_id = ast_id.ast_id.file_id;
977 let item_tree = self.db.file_item_tree(file_id);
978 let mod_dir = self.mod_dirs[&directive.module_id].clone();
979 self.skip_attrs.insert(InFile::new(file_id, *mod_item), *attr);
980 ModCollector {
981 def_collector: &mut *self,
982 macro_depth: directive.depth,
983 module_id: directive.module_id,
984 file_id,
985 item_tree: &item_tree,
986 mod_dir,
987 }
988 .collect(&[*mod_item]);
989
990 // Remove the original directive since we resolved it.
991 return false;
992 }
993 }
994 }
995
996 // Not resolved to a derive helper, so try to resolve as a macro.
997 // FIXME: not yet :)
927 } 998 }
928 } 999 }
929 1000
930 true 1001 true
931 }); 1002 });
932 self.unexpanded_macros = macros; 1003 // Attribute resolution can add unresolved macro invocations, so concatenate the lists.
1004 self.unresolved_macros.extend(macros);
933 1005
934 for (module_id, macro_call_id, depth) in resolved { 1006 for (module_id, macro_call_id, depth) in resolved {
935 self.collect_macro_expansion(module_id, macro_call_id, depth); 1007 self.collect_macro_expansion(module_id, macro_call_id, depth);
@@ -1000,7 +1072,7 @@ impl DefCollector<'_> {
1000 fn finish(mut self) -> DefMap { 1072 fn finish(mut self) -> DefMap {
1001 // Emit diagnostics for all remaining unexpanded macros. 1073 // Emit diagnostics for all remaining unexpanded macros.
1002 1074
1003 for directive in &self.unexpanded_macros { 1075 for directive in &self.unresolved_macros {
1004 match &directive.kind { 1076 match &directive.kind {
1005 MacroDirectiveKind::FnLike { ast_id, fragment } => match macro_call_as_call_id( 1077 MacroDirectiveKind::FnLike { ast_id, fragment } => match macro_call_as_call_id(
1006 ast_id, 1078 ast_id,
@@ -1102,7 +1174,7 @@ impl ModCollector<'_, '_> {
1102 1174
1103 // Prelude module is always considered to be `#[macro_use]`. 1175 // Prelude module is always considered to be `#[macro_use]`.
1104 if let Some(prelude_module) = self.def_collector.def_map.prelude { 1176 if let Some(prelude_module) = self.def_collector.def_map.prelude {
1105 if prelude_module.krate != self.def_collector.def_map.krate { 1177 if prelude_module.krate != krate {
1106 cov_mark::hit!(prelude_is_macro_use); 1178 cov_mark::hit!(prelude_is_macro_use);
1107 self.def_collector.import_all_macros_exported(self.module_id, prelude_module.krate); 1179 self.def_collector.import_all_macros_exported(self.module_id, prelude_module.krate);
1108 } 1180 }
@@ -1172,6 +1244,7 @@ impl ModCollector<'_, '_> {
1172 status: PartialResolvedImport::Unresolved, 1244 status: PartialResolvedImport::Unresolved,
1173 }) 1245 })
1174 } 1246 }
1247 ModItem::ExternBlock(block) => self.collect(&self.item_tree[block].children),
1175 ModItem::MacroCall(mac) => self.collect_macro_call(&self.item_tree[mac]), 1248 ModItem::MacroCall(mac) => self.collect_macro_call(&self.item_tree[mac]),
1176 ModItem::MacroRules(id) => self.collect_macro_rules(id), 1249 ModItem::MacroRules(id) => self.collect_macro_rules(id),
1177 ModItem::MacroDef(id) => self.collect_macro_def(id), 1250 ModItem::MacroDef(id) => self.collect_macro_def(id),
@@ -1203,28 +1276,18 @@ impl ModCollector<'_, '_> {
1203 ModItem::Struct(id) => { 1276 ModItem::Struct(id) => {
1204 let it = &self.item_tree[id]; 1277 let it = &self.item_tree[id];
1205 1278
1206 // FIXME: check attrs to see if this is an attribute macro invocation;
1207 // in which case we don't add the invocation, just a single attribute
1208 // macro invocation
1209 self.collect_derives(&attrs, it.ast_id.upcast());
1210
1211 def = Some(DefData { 1279 def = Some(DefData {
1212 id: StructLoc { container: module, id: ItemTreeId::new(self.file_id, id) } 1280 id: StructLoc { container: module, id: ItemTreeId::new(self.file_id, id) }
1213 .intern(self.def_collector.db) 1281 .intern(self.def_collector.db)
1214 .into(), 1282 .into(),
1215 name: &it.name, 1283 name: &it.name,
1216 visibility: &self.item_tree[it.visibility], 1284 visibility: &self.item_tree[it.visibility],
1217 has_constructor: it.kind != StructDefKind::Record, 1285 has_constructor: !matches!(it.fields, Fields::Record(_)),
1218 }); 1286 });
1219 } 1287 }
1220 ModItem::Union(id) => { 1288 ModItem::Union(id) => {
1221 let it = &self.item_tree[id]; 1289 let it = &self.item_tree[id];
1222 1290
1223 // FIXME: check attrs to see if this is an attribute macro invocation;
1224 // in which case we don't add the invocation, just a single attribute
1225 // macro invocation
1226 self.collect_derives(&attrs, it.ast_id.upcast());
1227
1228 def = Some(DefData { 1291 def = Some(DefData {
1229 id: UnionLoc { container: module, id: ItemTreeId::new(self.file_id, id) } 1292 id: UnionLoc { container: module, id: ItemTreeId::new(self.file_id, id) }
1230 .intern(self.def_collector.db) 1293 .intern(self.def_collector.db)
@@ -1237,11 +1300,6 @@ impl ModCollector<'_, '_> {
1237 ModItem::Enum(id) => { 1300 ModItem::Enum(id) => {
1238 let it = &self.item_tree[id]; 1301 let it = &self.item_tree[id];
1239 1302
1240 // FIXME: check attrs to see if this is an attribute macro invocation;
1241 // in which case we don't add the invocation, just a single attribute
1242 // macro invocation
1243 self.collect_derives(&attrs, it.ast_id.upcast());
1244
1245 def = Some(DefData { 1303 def = Some(DefData {
1246 id: EnumLoc { container: module, id: ItemTreeId::new(self.file_id, id) } 1304 id: EnumLoc { container: module, id: ItemTreeId::new(self.file_id, id) }
1247 .intern(self.def_collector.db) 1305 .intern(self.def_collector.db)
@@ -1453,76 +1511,116 @@ impl ModCollector<'_, '_> {
1453 /// 1511 ///
1454 /// Returns `Err` when some attributes could not be resolved to builtins and have been 1512 /// Returns `Err` when some attributes could not be resolved to builtins and have been
1455 /// registered as unresolved. 1513 /// registered as unresolved.
1514 ///
1515 /// If `ignore_up_to` is `Some`, attributes precending and including that attribute will be
1516 /// assumed to be resolved already.
1456 fn resolve_attributes(&mut self, attrs: &Attrs, mod_item: ModItem) -> Result<(), ()> { 1517 fn resolve_attributes(&mut self, attrs: &Attrs, mod_item: ModItem) -> Result<(), ()> {
1457 fn is_builtin_attr(path: &ModPath) -> bool { 1518 let mut ignore_up_to =
1458 if path.kind == PathKind::Plain { 1519 self.def_collector.skip_attrs.get(&InFile::new(self.file_id, mod_item)).copied();
1459 if let Some(tool_module) = path.segments().first() { 1520 let iter = attrs
1460 let tool_module = tool_module.to_string(); 1521 .iter()
1461 if builtin_attr::TOOL_MODULES.iter().any(|m| tool_module == *m) { 1522 .dedup_by(|a, b| {
1462 return true; 1523 // FIXME: this should not be required, all attributes on an item should have a
1463 } 1524 // unique ID!
1464 } 1525 // Still, this occurs because `#[cfg_attr]` can "expand" to multiple attributes:
1465 1526 // #[cfg_attr(not(off), unresolved, unresolved)]
1466 if let Some(name) = path.as_ident() { 1527 // struct S;
1467 let name = name.to_string(); 1528 // We should come up with a different way to ID attributes.
1468 if builtin_attr::INERT_ATTRIBUTES 1529 a.id == b.id
1469 .iter() 1530 })
1470 .chain(builtin_attr::EXTRA_ATTRIBUTES) 1531 .skip_while(|attr| match ignore_up_to {
1471 .any(|attr| name == *attr) 1532 Some(id) if attr.id == id => {
1472 { 1533 ignore_up_to = None;
1473 return true; 1534 true
1474 }
1475 } 1535 }
1476 } 1536 Some(_) => true,
1477 1537 None => false,
1478 false 1538 });
1479 } 1539
1480 1540 for attr in iter {
1481 // We failed to resolve an attribute on this item earlier, and are falling back to treating 1541 if attr.path.as_ident() == Some(&hir_expand::name![derive]) {
1482 // the item as-is. 1542 self.collect_derive(attr, mod_item);
1483 if self.def_collector.ignore_attrs_on.contains(&InFile::new(self.file_id, mod_item)) { 1543 } else if self.is_builtin_or_registered_attr(&attr.path) {
1484 return Ok(()); 1544 continue;
1485 } 1545 } else {
1486 1546 log::debug!("non-builtin attribute {}", attr.path);
1487 match attrs.iter().find(|attr| !is_builtin_attr(&attr.path)) {
1488 Some(non_builtin_attr) => {
1489 log::debug!("non-builtin attribute {}", non_builtin_attr.path);
1490 1547
1491 let ast_id = AstIdWithPath::new( 1548 let ast_id = AstIdWithPath::new(
1492 self.file_id, 1549 self.file_id,
1493 mod_item.ast_id(self.item_tree), 1550 mod_item.ast_id(self.item_tree),
1494 non_builtin_attr.path.as_ref().clone(), 1551 attr.path.as_ref().clone(),
1495 ); 1552 );
1496 self.def_collector.unexpanded_macros.push(MacroDirective { 1553 self.def_collector.unresolved_macros.push(MacroDirective {
1497 module_id: self.module_id, 1554 module_id: self.module_id,
1498 depth: self.macro_depth + 1, 1555 depth: self.macro_depth + 1,
1499 kind: MacroDirectiveKind::Attr { ast_id, attr: non_builtin_attr.id, mod_item }, 1556 kind: MacroDirectiveKind::Attr { ast_id, attr: attr.id, mod_item },
1500 }); 1557 });
1501 1558
1502 Err(()) 1559 return Err(());
1503 } 1560 }
1504 None => Ok(()),
1505 } 1561 }
1562
1563 Ok(())
1506 } 1564 }
1507 1565
1508 fn collect_derives(&mut self, attrs: &Attrs, ast_id: FileAstId<ast::Item>) { 1566 fn is_builtin_or_registered_attr(&self, path: &ModPath) -> bool {
1509 for derive in attrs.by_key("derive").attrs() { 1567 if path.kind == PathKind::Plain {
1510 match derive.parse_derive() { 1568 if let Some(tool_module) = path.segments().first() {
1511 Some(derive_macros) => { 1569 let tool_module = tool_module.to_string();
1512 for path in derive_macros { 1570 if builtin_attr::TOOL_MODULES
1513 let ast_id = AstIdWithPath::new(self.file_id, ast_id, path); 1571 .iter()
1514 self.def_collector.unexpanded_macros.push(MacroDirective { 1572 .copied()
1515 module_id: self.module_id, 1573 .chain(self.def_collector.registered_tools.iter().map(|s| &**s))
1516 depth: self.macro_depth + 1, 1574 .any(|m| tool_module == *m)
1517 kind: MacroDirectiveKind::Derive { ast_id, derive_attr: derive.id }, 1575 {
1518 }); 1576 return true;
1519 }
1520 } 1577 }
1521 None => { 1578 }
1522 // FIXME: diagnose 1579
1523 log::debug!("malformed derive: {:?}", derive); 1580 if let Some(name) = path.as_ident() {
1581 let name = name.to_string();
1582 if builtin_attr::INERT_ATTRIBUTES
1583 .iter()
1584 .chain(builtin_attr::EXTRA_ATTRIBUTES)
1585 .copied()
1586 .chain(self.def_collector.registered_attrs.iter().map(|s| &**s))
1587 .any(|attr| name == *attr)
1588 {
1589 return true;
1590 }
1591 }
1592 }
1593
1594 false
1595 }
1596
1597 fn collect_derive(&mut self, attr: &Attr, mod_item: ModItem) {
1598 let ast_id: FileAstId<ast::Item> = match mod_item {
1599 ModItem::Struct(it) => self.item_tree[it].ast_id.upcast(),
1600 ModItem::Union(it) => self.item_tree[it].ast_id.upcast(),
1601 ModItem::Enum(it) => self.item_tree[it].ast_id.upcast(),
1602 _ => {
1603 // Cannot use derive on this item.
1604 // FIXME: diagnose
1605 return;
1606 }
1607 };
1608
1609 match attr.parse_derive() {
1610 Some(derive_macros) => {
1611 for path in derive_macros {
1612 let ast_id = AstIdWithPath::new(self.file_id, ast_id, path);
1613 self.def_collector.unresolved_macros.push(MacroDirective {
1614 module_id: self.module_id,
1615 depth: self.macro_depth + 1,
1616 kind: MacroDirectiveKind::Derive { ast_id, derive_attr: attr.id },
1617 });
1524 } 1618 }
1525 } 1619 }
1620 None => {
1621 // FIXME: diagnose
1622 log::debug!("malformed derive: {:?}", attr);
1623 }
1526 } 1624 }
1527 } 1625 }
1528 1626
@@ -1686,7 +1784,7 @@ impl ModCollector<'_, '_> {
1686 ast_id.path.kind = PathKind::Super(0); 1784 ast_id.path.kind = PathKind::Super(0);
1687 } 1785 }
1688 1786
1689 self.def_collector.unexpanded_macros.push(MacroDirective { 1787 self.def_collector.unresolved_macros.push(MacroDirective {
1690 module_id: self.module_id, 1788 module_id: self.module_id,
1691 depth: self.macro_depth + 1, 1789 depth: self.macro_depth + 1,
1692 kind: MacroDirectiveKind::FnLike { ast_id, fragment: mac.fragment }, 1790 kind: MacroDirectiveKind::FnLike { ast_id, fragment: mac.fragment },
@@ -1731,14 +1829,16 @@ mod tests {
1731 glob_imports: FxHashMap::default(), 1829 glob_imports: FxHashMap::default(),
1732 unresolved_imports: Vec::new(), 1830 unresolved_imports: Vec::new(),
1733 resolved_imports: Vec::new(), 1831 resolved_imports: Vec::new(),
1734 unexpanded_macros: Vec::new(), 1832 unresolved_macros: Vec::new(),
1735 mod_dirs: FxHashMap::default(), 1833 mod_dirs: FxHashMap::default(),
1736 cfg_options: &CfgOptions::default(), 1834 cfg_options: &CfgOptions::default(),
1737 proc_macros: Default::default(), 1835 proc_macros: Default::default(),
1738 exports_proc_macros: false, 1836 exports_proc_macros: false,
1739 from_glob_import: Default::default(), 1837 from_glob_import: Default::default(),
1740 ignore_attrs_on: FxHashSet::default(), 1838 skip_attrs: Default::default(),
1741 derive_helpers_in_scope: FxHashMap::default(), 1839 derive_helpers_in_scope: Default::default(),
1840 registered_attrs: Default::default(),
1841 registered_tools: Default::default(),
1742 }; 1842 };
1743 collector.seed_with_top_level(); 1843 collector.seed_with_top_level();
1744 collector.collect(); 1844 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}
diff --git a/crates/hir_def/src/nameres/tests/macros.rs b/crates/hir_def/src/nameres/tests/macros.rs
index 6eb5f97be..3065efd65 100644
--- a/crates/hir_def/src/nameres/tests/macros.rs
+++ b/crates/hir_def/src/nameres/tests/macros.rs
@@ -736,6 +736,80 @@ fn unresolved_attributes_fall_back_track_per_file_moditems() {
736} 736}
737 737
738#[test] 738#[test]
739fn unresolved_attrs_extern_block_hang() {
740 // Regression test for https://github.com/rust-analyzer/rust-analyzer/issues/8905
741 check(
742 r#"
743#[unresolved]
744extern "C" {
745 #[unresolved]
746 fn f();
747}
748 "#,
749 expect![[r#"
750 crate
751 f: v
752 "#]],
753 );
754}
755
756#[test]
757fn macros_in_extern_block() {
758 check(
759 r#"
760macro_rules! m {
761 () => { static S: u8; };
762}
763
764extern {
765 m!();
766}
767 "#,
768 expect![[r#"
769 crate
770 S: v
771 "#]],
772 );
773}
774
775#[test]
776fn resolves_derive_helper() {
777 cov_mark::check!(resolved_derive_helper);
778 check(
779 r#"
780//- /main.rs crate:main deps:proc
781#[derive(proc::Derive)]
782#[helper]
783#[unresolved]
784struct S;
785
786//- /proc.rs crate:proc
787#[proc_macro_derive(Derive, attributes(helper))]
788fn derive() {}
789 "#,
790 expect![[r#"
791 crate
792 S: t v
793 "#]],
794 );
795}
796
797#[test]
798fn unresolved_attr_with_cfg_attr_hang() {
799 // Another regression test for https://github.com/rust-analyzer/rust-analyzer/issues/8905
800 check(
801 r#"
802#[cfg_attr(not(off), unresolved, unresolved)]
803struct S;
804 "#,
805 expect![[r#"
806 crate
807 S: t v
808 "#]],
809 );
810}
811
812#[test]
739fn macro_expansion_overflow() { 813fn macro_expansion_overflow() {
740 cov_mark::check!(macro_expansion_overflow); 814 cov_mark::check!(macro_expansion_overflow);
741 check( 815 check(