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.rs194
-rw-r--r--crates/hir_def/src/nameres/tests/macros.rs22
2 files changed, 141 insertions, 75 deletions
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs
index 3896be25d..2d1cba632 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::{AttrId, Attrs}, 23 attr::{Attr, AttrId, 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,
@@ -94,14 +94,14 @@ pub(super) fn collect_defs(
94 unresolved_imports: Vec::new(), 94 unresolved_imports: Vec::new(),
95 resolved_imports: Vec::new(), 95 resolved_imports: Vec::new(),
96 96
97 unexpanded_macros: Vec::new(), 97 unresolved_macros: Vec::new(),
98 mod_dirs: FxHashMap::default(), 98 mod_dirs: FxHashMap::default(),
99 cfg_options, 99 cfg_options,
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: FxHashSet::default(), 103 ignore_attrs_on: Default::default(),
104 derive_helpers_in_scope: FxHashMap::default(), 104 derive_helpers_in_scope: Default::default(),
105 }; 105 };
106 match block { 106 match block {
107 Some(block) => { 107 Some(block) => {
@@ -237,7 +237,7 @@ struct DefCollector<'a> {
237 glob_imports: FxHashMap<LocalModuleId, Vec<(LocalModuleId, Visibility)>>, 237 glob_imports: FxHashMap<LocalModuleId, Vec<(LocalModuleId, Visibility)>>,
238 unresolved_imports: Vec<ImportDirective>, 238 unresolved_imports: Vec<ImportDirective>,
239 resolved_imports: Vec<ImportDirective>, 239 resolved_imports: Vec<ImportDirective>,
240 unexpanded_macros: Vec<MacroDirective>, 240 unresolved_macros: Vec<MacroDirective>,
241 mod_dirs: FxHashMap<LocalModuleId, ModDir>, 241 mod_dirs: FxHashMap<LocalModuleId, ModDir>,
242 cfg_options: &'a CfgOptions, 242 cfg_options: &'a CfgOptions,
243 /// List of procedural macros defined by this crate. This is read from the dynamic library 243 /// List of procedural macros defined by this crate. This is read from the dynamic library
@@ -247,7 +247,13 @@ struct DefCollector<'a> {
247 proc_macros: Vec<(Name, ProcMacroExpander)>, 247 proc_macros: Vec<(Name, ProcMacroExpander)>,
248 exports_proc_macros: bool, 248 exports_proc_macros: bool,
249 from_glob_import: PerNsGlobImports, 249 from_glob_import: PerNsGlobImports,
250 ignore_attrs_on: FxHashSet<InFile<ModItem>>, 250 /// If we fail to resolve an attribute on a `ModItem`, we fall back to ignoring the attribute.
251 /// This map is used to skip all attributes up to and including the one that failed to resolve,
252 /// in order to not expand them twice.
253 ///
254 /// This also stores the attributes to skip when we resolve derive helpers and non-macro
255 /// non-builtin attributes in general.
256 ignore_attrs_on: FxHashMap<InFile<ModItem>, AttrId>,
251 /// Tracks which custom derives are in scope for an item, to allow resolution of derive helper 257 /// Tracks which custom derives are in scope for an item, to allow resolution of derive helper
252 /// attributes. 258 /// attributes.
253 derive_helpers_in_scope: FxHashMap<AstId<ast::Item>, Vec<Name>>, 259 derive_helpers_in_scope: FxHashMap<AstId<ast::Item>, Vec<Name>>,
@@ -319,7 +325,7 @@ impl DefCollector<'_> {
319 } 325 }
320 } 326 }
321 327
322 if self.reseed_with_unresolved_attributes() == ReachedFixedPoint::Yes { 328 if self.reseed_with_unresolved_attribute() == ReachedFixedPoint::Yes {
323 break; 329 break;
324 } 330 }
325 } 331 }
@@ -362,25 +368,21 @@ impl DefCollector<'_> {
362 } 368 }
363 369
364 /// When the fixed-point loop reaches a stable state, we might still have some unresolved 370 /// 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 371 /// attributes (or unexpanded attribute proc macros) left over. This takes one of them, and
366 /// item they're applied to back into name resolution. 372 /// feeds the item it's applied to back into name resolution.
367 /// 373 ///
368 /// This effectively ignores the fact that the macro is there and just treats the items as 374 /// This effectively ignores the fact that the macro is there and just treats the items as
369 /// normal code. 375 /// normal code.
370 /// 376 ///
371 /// This improves UX when proc macros are turned off or don't work, and replicates the behavior 377 /// This improves UX when proc macros are turned off or don't work, and replicates the behavior
372 /// before we supported proc. attribute macros. 378 /// before we supported proc. attribute macros.
373 fn reseed_with_unresolved_attributes(&mut self) -> ReachedFixedPoint { 379 fn reseed_with_unresolved_attribute(&mut self) -> ReachedFixedPoint {
374 cov_mark::hit!(unresolved_attribute_fallback); 380 cov_mark::hit!(unresolved_attribute_fallback);
375 381
376 let mut added_items = false; 382 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()); 383 let pos = unresolved_macros.iter().position(|directive| {
378 for directive in &unexpanded_macros { 384 if let MacroDirectiveKind::Attr { ast_id, mod_item, attr } = &directive.kind {
379 if let MacroDirectiveKind::Attr { ast_id, mod_item, .. } = &directive.kind { 385 self.ignore_attrs_on.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 386
385 let file_id = self.def_map[directive.module_id].definition_source(self.db).file_id; 387 let file_id = self.def_map[directive.module_id].definition_source(self.db).file_id;
386 let item_tree = self.db.file_item_tree(file_id); 388 let item_tree = self.db.file_item_tree(file_id);
@@ -394,14 +396,20 @@ impl DefCollector<'_> {
394 mod_dir, 396 mod_dir,
395 } 397 }
396 .collect(&[*mod_item]); 398 .collect(&[*mod_item]);
397 added_items = true; 399 true
400 } else {
401 false
398 } 402 }
403 });
404
405 if let Some(pos) = pos {
406 unresolved_macros.remove(pos);
399 } 407 }
400 408
401 // The collection above might add new unresolved macros (eg. derives), so merge the lists. 409 // The collection above might add new unresolved macros (eg. derives), so merge the lists.
402 self.unexpanded_macros.extend(unexpanded_macros); 410 self.unresolved_macros.extend(unresolved_macros);
403 411
404 if added_items { 412 if pos.is_some() {
405 // Continue name resolution with the new data. 413 // Continue name resolution with the new data.
406 ReachedFixedPoint::No 414 ReachedFixedPoint::No
407 } else { 415 } else {
@@ -873,7 +881,7 @@ impl DefCollector<'_> {
873 } 881 }
874 882
875 fn resolve_macros(&mut self) -> ReachedFixedPoint { 883 fn resolve_macros(&mut self) -> ReachedFixedPoint {
876 let mut macros = std::mem::replace(&mut self.unexpanded_macros, Vec::new()); 884 let mut macros = std::mem::replace(&mut self.unresolved_macros, Vec::new());
877 let mut resolved = Vec::new(); 885 let mut resolved = Vec::new();
878 let mut res = ReachedFixedPoint::Yes; 886 let mut res = ReachedFixedPoint::Yes;
879 macros.retain(|directive| { 887 macros.retain(|directive| {
@@ -922,14 +930,45 @@ impl DefCollector<'_> {
922 Err(UnresolvedMacro { .. }) => (), 930 Err(UnresolvedMacro { .. }) => (),
923 } 931 }
924 } 932 }
925 MacroDirectiveKind::Attr { .. } => { 933 MacroDirectiveKind::Attr { ast_id, mod_item, attr } => {
926 // not yet :) 934 if let Some(ident) = ast_id.path.as_ident() {
935 if let Some(helpers) = self.derive_helpers_in_scope.get(&ast_id.ast_id) {
936 if helpers.contains(ident) {
937 cov_mark::hit!(resolved_derive_helper);
938
939 // Resolved to derive helper. Collect the item's attributes again,
940 // starting after the derive helper.
941 let file_id = self.def_map[directive.module_id]
942 .definition_source(self.db)
943 .file_id;
944 let item_tree = self.db.file_item_tree(file_id);
945 let mod_dir = self.mod_dirs[&directive.module_id].clone();
946 self.ignore_attrs_on.insert(InFile::new(file_id, *mod_item), *attr);
947 ModCollector {
948 def_collector: &mut *self,
949 macro_depth: directive.depth,
950 module_id: directive.module_id,
951 file_id,
952 item_tree: &item_tree,
953 mod_dir,
954 }
955 .collect(&[*mod_item]);
956
957 // Remove the original directive since we resolved it.
958 return false;
959 }
960 }
961 }
962
963 // Not resolved to a derive helper, so try to resolve as a macro.
964 // FIXME: not yet :)
927 } 965 }
928 } 966 }
929 967
930 true 968 true
931 }); 969 });
932 self.unexpanded_macros = macros; 970 // Attribute resolution can add unresolved macro invocations, so concatenate the lists.
971 self.unresolved_macros.extend(macros);
933 972
934 for (module_id, macro_call_id, depth) in resolved { 973 for (module_id, macro_call_id, depth) in resolved {
935 self.collect_macro_expansion(module_id, macro_call_id, depth); 974 self.collect_macro_expansion(module_id, macro_call_id, depth);
@@ -1000,7 +1039,7 @@ impl DefCollector<'_> {
1000 fn finish(mut self) -> DefMap { 1039 fn finish(mut self) -> DefMap {
1001 // Emit diagnostics for all remaining unexpanded macros. 1040 // Emit diagnostics for all remaining unexpanded macros.
1002 1041
1003 for directive in &self.unexpanded_macros { 1042 for directive in &self.unresolved_macros {
1004 match &directive.kind { 1043 match &directive.kind {
1005 MacroDirectiveKind::FnLike { ast_id, fragment } => match macro_call_as_call_id( 1044 MacroDirectiveKind::FnLike { ast_id, fragment } => match macro_call_as_call_id(
1006 ast_id, 1045 ast_id,
@@ -1102,7 +1141,7 @@ impl ModCollector<'_, '_> {
1102 1141
1103 // Prelude module is always considered to be `#[macro_use]`. 1142 // Prelude module is always considered to be `#[macro_use]`.
1104 if let Some(prelude_module) = self.def_collector.def_map.prelude { 1143 if let Some(prelude_module) = self.def_collector.def_map.prelude {
1105 if prelude_module.krate != self.def_collector.def_map.krate { 1144 if prelude_module.krate != krate {
1106 cov_mark::hit!(prelude_is_macro_use); 1145 cov_mark::hit!(prelude_is_macro_use);
1107 self.def_collector.import_all_macros_exported(self.module_id, prelude_module.krate); 1146 self.def_collector.import_all_macros_exported(self.module_id, prelude_module.krate);
1108 } 1147 }
@@ -1203,11 +1242,6 @@ impl ModCollector<'_, '_> {
1203 ModItem::Struct(id) => { 1242 ModItem::Struct(id) => {
1204 let it = &self.item_tree[id]; 1243 let it = &self.item_tree[id];
1205 1244
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 { 1245 def = Some(DefData {
1212 id: StructLoc { container: module, id: ItemTreeId::new(self.file_id, id) } 1246 id: StructLoc { container: module, id: ItemTreeId::new(self.file_id, id) }
1213 .intern(self.def_collector.db) 1247 .intern(self.def_collector.db)
@@ -1220,11 +1254,6 @@ impl ModCollector<'_, '_> {
1220 ModItem::Union(id) => { 1254 ModItem::Union(id) => {
1221 let it = &self.item_tree[id]; 1255 let it = &self.item_tree[id];
1222 1256
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 { 1257 def = Some(DefData {
1229 id: UnionLoc { container: module, id: ItemTreeId::new(self.file_id, id) } 1258 id: UnionLoc { container: module, id: ItemTreeId::new(self.file_id, id) }
1230 .intern(self.def_collector.db) 1259 .intern(self.def_collector.db)
@@ -1237,11 +1266,6 @@ impl ModCollector<'_, '_> {
1237 ModItem::Enum(id) => { 1266 ModItem::Enum(id) => {
1238 let it = &self.item_tree[id]; 1267 let it = &self.item_tree[id];
1239 1268
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 { 1269 def = Some(DefData {
1246 id: EnumLoc { container: module, id: ItemTreeId::new(self.file_id, id) } 1270 id: EnumLoc { container: module, id: ItemTreeId::new(self.file_id, id) }
1247 .intern(self.def_collector.db) 1271 .intern(self.def_collector.db)
@@ -1453,6 +1477,9 @@ impl ModCollector<'_, '_> {
1453 /// 1477 ///
1454 /// Returns `Err` when some attributes could not be resolved to builtins and have been 1478 /// Returns `Err` when some attributes could not be resolved to builtins and have been
1455 /// registered as unresolved. 1479 /// registered as unresolved.
1480 ///
1481 /// If `ignore_up_to` is `Some`, attributes precending and including that attribute will be
1482 /// assumed to be resolved already.
1456 fn resolve_attributes(&mut self, attrs: &Attrs, mod_item: ModItem) -> Result<(), ()> { 1483 fn resolve_attributes(&mut self, attrs: &Attrs, mod_item: ModItem) -> Result<(), ()> {
1457 fn is_builtin_attr(path: &ModPath) -> bool { 1484 fn is_builtin_attr(path: &ModPath) -> bool {
1458 if path.kind == PathKind::Plain { 1485 if path.kind == PathKind::Plain {
@@ -1478,51 +1505,68 @@ impl ModCollector<'_, '_> {
1478 false 1505 false
1479 } 1506 }
1480 1507
1481 // We failed to resolve an attribute on this item earlier, and are falling back to treating 1508 let mut ignore_up_to =
1482 // the item as-is. 1509 self.def_collector.ignore_attrs_on.get(&InFile::new(self.file_id, mod_item)).copied();
1483 if self.def_collector.ignore_attrs_on.contains(&InFile::new(self.file_id, mod_item)) { 1510 for attr in attrs.iter().skip_while(|attr| match ignore_up_to {
1484 return Ok(()); 1511 Some(id) if attr.id == id => {
1485 } 1512 ignore_up_to = None;
1486 1513 true
1487 match attrs.iter().find(|attr| !is_builtin_attr(&attr.path)) { 1514 }
1488 Some(non_builtin_attr) => { 1515 Some(_) => true,
1489 log::debug!("non-builtin attribute {}", non_builtin_attr.path); 1516 None => false,
1517 }) {
1518 if attr.path.as_ident() == Some(&hir_expand::name![derive]) {
1519 self.collect_derive(attr, mod_item);
1520 } else if is_builtin_attr(&attr.path) {
1521 continue;
1522 } else {
1523 log::debug!("non-builtin attribute {}", attr.path);
1490 1524
1491 let ast_id = AstIdWithPath::new( 1525 let ast_id = AstIdWithPath::new(
1492 self.file_id, 1526 self.file_id,
1493 mod_item.ast_id(self.item_tree), 1527 mod_item.ast_id(self.item_tree),
1494 non_builtin_attr.path.as_ref().clone(), 1528 attr.path.as_ref().clone(),
1495 ); 1529 );
1496 self.def_collector.unexpanded_macros.push(MacroDirective { 1530 self.def_collector.unresolved_macros.push(MacroDirective {
1497 module_id: self.module_id, 1531 module_id: self.module_id,
1498 depth: self.macro_depth + 1, 1532 depth: self.macro_depth + 1,
1499 kind: MacroDirectiveKind::Attr { ast_id, attr: non_builtin_attr.id, mod_item }, 1533 kind: MacroDirectiveKind::Attr { ast_id, attr: attr.id, mod_item },
1500 }); 1534 });
1501 1535
1502 Err(()) 1536 return Err(());
1503 } 1537 }
1504 None => Ok(()),
1505 } 1538 }
1539
1540 Ok(())
1506 } 1541 }
1507 1542
1508 fn collect_derives(&mut self, attrs: &Attrs, ast_id: FileAstId<ast::Item>) { 1543 fn collect_derive(&mut self, attr: &Attr, mod_item: ModItem) {
1509 for derive in attrs.by_key("derive").attrs() { 1544 let ast_id: FileAstId<ast::Item> = match mod_item {
1510 match derive.parse_derive() { 1545 ModItem::Struct(it) => self.item_tree[it].ast_id.upcast(),
1511 Some(derive_macros) => { 1546 ModItem::Union(it) => self.item_tree[it].ast_id.upcast(),
1512 for path in derive_macros { 1547 ModItem::Enum(it) => self.item_tree[it].ast_id.upcast(),
1513 let ast_id = AstIdWithPath::new(self.file_id, ast_id, path); 1548 _ => {
1514 self.def_collector.unexpanded_macros.push(MacroDirective { 1549 // Cannot use derive on this item.
1515 module_id: self.module_id, 1550 // FIXME: diagnose
1516 depth: self.macro_depth + 1, 1551 return;
1517 kind: MacroDirectiveKind::Derive { ast_id, derive_attr: derive.id }, 1552 }
1518 }); 1553 };
1519 } 1554
1520 } 1555 match attr.parse_derive() {
1521 None => { 1556 Some(derive_macros) => {
1522 // FIXME: diagnose 1557 for path in derive_macros {
1523 log::debug!("malformed derive: {:?}", derive); 1558 let ast_id = AstIdWithPath::new(self.file_id, ast_id, path);
1559 self.def_collector.unresolved_macros.push(MacroDirective {
1560 module_id: self.module_id,
1561 depth: self.macro_depth + 1,
1562 kind: MacroDirectiveKind::Derive { ast_id, derive_attr: attr.id },
1563 });
1524 } 1564 }
1525 } 1565 }
1566 None => {
1567 // FIXME: diagnose
1568 log::debug!("malformed derive: {:?}", attr);
1569 }
1526 } 1570 }
1527 } 1571 }
1528 1572
@@ -1686,7 +1730,7 @@ impl ModCollector<'_, '_> {
1686 ast_id.path.kind = PathKind::Super(0); 1730 ast_id.path.kind = PathKind::Super(0);
1687 } 1731 }
1688 1732
1689 self.def_collector.unexpanded_macros.push(MacroDirective { 1733 self.def_collector.unresolved_macros.push(MacroDirective {
1690 module_id: self.module_id, 1734 module_id: self.module_id,
1691 depth: self.macro_depth + 1, 1735 depth: self.macro_depth + 1,
1692 kind: MacroDirectiveKind::FnLike { ast_id, fragment: mac.fragment }, 1736 kind: MacroDirectiveKind::FnLike { ast_id, fragment: mac.fragment },
@@ -1731,13 +1775,13 @@ mod tests {
1731 glob_imports: FxHashMap::default(), 1775 glob_imports: FxHashMap::default(),
1732 unresolved_imports: Vec::new(), 1776 unresolved_imports: Vec::new(),
1733 resolved_imports: Vec::new(), 1777 resolved_imports: Vec::new(),
1734 unexpanded_macros: Vec::new(), 1778 unresolved_macros: Vec::new(),
1735 mod_dirs: FxHashMap::default(), 1779 mod_dirs: FxHashMap::default(),
1736 cfg_options: &CfgOptions::default(), 1780 cfg_options: &CfgOptions::default(),
1737 proc_macros: Default::default(), 1781 proc_macros: Default::default(),
1738 exports_proc_macros: false, 1782 exports_proc_macros: false,
1739 from_glob_import: Default::default(), 1783 from_glob_import: Default::default(),
1740 ignore_attrs_on: FxHashSet::default(), 1784 ignore_attrs_on: Default::default(),
1741 derive_helpers_in_scope: FxHashMap::default(), 1785 derive_helpers_in_scope: FxHashMap::default(),
1742 }; 1786 };
1743 collector.seed_with_top_level(); 1787 collector.seed_with_top_level();
diff --git a/crates/hir_def/src/nameres/tests/macros.rs b/crates/hir_def/src/nameres/tests/macros.rs
index 6eb5f97be..04de107f5 100644
--- a/crates/hir_def/src/nameres/tests/macros.rs
+++ b/crates/hir_def/src/nameres/tests/macros.rs
@@ -736,6 +736,28 @@ fn unresolved_attributes_fall_back_track_per_file_moditems() {
736} 736}
737 737
738#[test] 738#[test]
739fn resolves_derive_helper() {
740 cov_mark::check!(resolved_derive_helper);
741 check(
742 r#"
743//- /main.rs crate:main deps:proc
744#[derive(proc::Derive)]
745#[helper]
746#[unresolved]
747struct S;
748
749//- /proc.rs crate:proc
750#[proc_macro_derive(Derive, attributes(helper))]
751fn derive() {}
752 "#,
753 expect![[r#"
754 crate
755 S: t v
756 "#]],
757 )
758}
759
760#[test]
739fn macro_expansion_overflow() { 761fn macro_expansion_overflow() {
740 cov_mark::check!(macro_expansion_overflow); 762 cov_mark::check!(macro_expansion_overflow);
741 check( 763 check(