aboutsummaryrefslogtreecommitdiff
path: root/crates/hir_def
diff options
context:
space:
mode:
Diffstat (limited to 'crates/hir_def')
-rw-r--r--crates/hir_def/src/intern.rs8
-rw-r--r--crates/hir_def/src/nameres/collector.rs180
2 files changed, 160 insertions, 28 deletions
diff --git a/crates/hir_def/src/intern.rs b/crates/hir_def/src/intern.rs
index abc304ef0..5cc7f2df6 100644
--- a/crates/hir_def/src/intern.rs
+++ b/crates/hir_def/src/intern.rs
@@ -4,7 +4,7 @@
4 4
5use std::{ 5use std::{
6 collections::HashMap, 6 collections::HashMap,
7 fmt::{self, Debug}, 7 fmt::{self, Debug, Display},
8 hash::{BuildHasherDefault, Hash, Hasher}, 8 hash::{BuildHasherDefault, Hash, Hasher},
9 ops::Deref, 9 ops::Deref,
10 sync::Arc, 10 sync::Arc,
@@ -171,6 +171,12 @@ impl<T: Debug + Internable + ?Sized> Debug for Interned<T> {
171 } 171 }
172} 172}
173 173
174impl<T: Display + Internable + ?Sized> Display for Interned<T> {
175 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
176 (*self.arc).fmt(f)
177 }
178}
179
174pub struct InternStorage<T: ?Sized> { 180pub struct InternStorage<T: ?Sized> {
175 map: OnceCell<InternMap<T>>, 181 map: OnceCell<InternMap<T>>,
176} 182}
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs
index d4840be2f..221a5a556 100644
--- a/crates/hir_def/src/nameres/collector.rs
+++ b/crates/hir_def/src/nameres/collector.rs
@@ -21,6 +21,7 @@ use syntax::ast;
21 21
22use crate::{ 22use crate::{
23 attr::{AttrId, Attrs}, 23 attr::{AttrId, Attrs},
24 builtin_attr,
24 db::DefDatabase, 25 db::DefDatabase,
25 derive_macro_as_call_id, 26 derive_macro_as_call_id,
26 intern::Interned, 27 intern::Interned,
@@ -99,6 +100,7 @@ pub(super) fn collect_defs(
99 proc_macros, 100 proc_macros,
100 exports_proc_macros: false, 101 exports_proc_macros: false,
101 from_glob_import: Default::default(), 102 from_glob_import: Default::default(),
103 ignore_attrs_on: FxHashSet::default(),
102 }; 104 };
103 match block { 105 match block {
104 Some(block) => { 106 Some(block) => {
@@ -217,6 +219,7 @@ struct MacroDirective {
217enum MacroDirectiveKind { 219enum MacroDirectiveKind {
218 FnLike { ast_id: AstIdWithPath<ast::MacroCall>, fragment: FragmentKind }, 220 FnLike { ast_id: AstIdWithPath<ast::MacroCall>, fragment: FragmentKind },
219 Derive { ast_id: AstIdWithPath<ast::Item>, derive_attr: AttrId }, 221 Derive { ast_id: AstIdWithPath<ast::Item>, derive_attr: AttrId },
222 Attr { ast_id: AstIdWithPath<ast::Item>, attr: AttrId, mod_item: ModItem },
220} 223}
221 224
222struct DefData<'a> { 225struct DefData<'a> {
@@ -243,6 +246,7 @@ struct DefCollector<'a> {
243 proc_macros: Vec<(Name, ProcMacroExpander)>, 246 proc_macros: Vec<(Name, ProcMacroExpander)>,
244 exports_proc_macros: bool, 247 exports_proc_macros: bool,
245 from_glob_import: PerNsGlobImports, 248 from_glob_import: PerNsGlobImports,
249 ignore_attrs_on: FxHashSet<ModItem>,
246} 250}
247 251
248impl DefCollector<'_> { 252impl DefCollector<'_> {
@@ -292,16 +296,26 @@ impl DefCollector<'_> {
292 fn collect(&mut self) { 296 fn collect(&mut self) {
293 // main name resolution fixed-point loop. 297 // main name resolution fixed-point loop.
294 let mut i = 0; 298 let mut i = 0;
295 loop { 299 'outer: loop {
296 self.db.check_canceled(); 300 loop {
297 self.resolve_imports(); 301 self.db.check_canceled();
302 loop {
303 if self.resolve_imports() == ReachedFixedPoint::Yes {
304 break;
305 }
306 }
307 if self.resolve_macros() == ReachedFixedPoint::Yes {
308 break;
309 }
298 310
299 match self.resolve_macros() { 311 i += 1;
300 ReachedFixedPoint::Yes => break, 312 if i == FIXED_POINT_LIMIT {
301 ReachedFixedPoint::No => i += 1, 313 log::error!("name resolution is stuck");
314 break 'outer;
315 }
302 } 316 }
303 if i == FIXED_POINT_LIMIT { 317
304 log::error!("name resolution is stuck"); 318 if self.reseed_with_unresolved_attributes() == ReachedFixedPoint::Yes {
305 break; 319 break;
306 } 320 }
307 } 321 }
@@ -343,6 +357,50 @@ impl DefCollector<'_> {
343 } 357 }
344 } 358 }
345 359
360 /// When the fixed-point loop reaches a stable state, we might still have some unresolved
361 /// attributes (or unexpanded attribute proc macros) left over. This takes them, and feeds the
362 /// item they're applied to back into name resolution.
363 ///
364 /// This effectively ignores the fact that the macro is there and just treats the items as
365 /// normal code.
366 ///
367 /// This improves UX when proc macros are turned off or don't work, and replicates the behavior
368 /// before we supported proc. attribute macros.
369 fn reseed_with_unresolved_attributes(&mut self) -> ReachedFixedPoint {
370 let mut added_items = false;
371 let unexpanded_macros = std::mem::replace(&mut self.unexpanded_macros, Vec::new());
372 for directive in &unexpanded_macros {
373 if let MacroDirectiveKind::Attr { mod_item, .. } = &directive.kind {
374 // Make sure to only add such items once.
375 if !self.ignore_attrs_on.insert(*mod_item) {
376 continue;
377 }
378
379 let file_id = self.def_map[directive.module_id].definition_source(self.db).file_id;
380 let item_tree = self.db.file_item_tree(file_id);
381 let mod_dir = self.mod_dirs[&directive.module_id].clone();
382 ModCollector {
383 def_collector: &mut *self,
384 macro_depth: directive.depth,
385 module_id: directive.module_id,
386 file_id,
387 item_tree: &item_tree,
388 mod_dir,
389 }
390 .collect(&[*mod_item]);
391 added_items = true;
392 }
393 }
394 self.unexpanded_macros = unexpanded_macros;
395
396 if added_items {
397 // Continue name resolution with the new data.
398 ReachedFixedPoint::No
399 } else {
400 ReachedFixedPoint::Yes
401 }
402 }
403
346 /// Adds a definition of procedural macro `name` to the root module. 404 /// Adds a definition of procedural macro `name` to the root module.
347 /// 405 ///
348 /// # Notes on procedural macro resolution 406 /// # Notes on procedural macro resolution
@@ -504,35 +562,35 @@ impl DefCollector<'_> {
504 } 562 }
505 } 563 }
506 564
507 /// Import resolution 565 /// Tries to resolve every currently unresolved import.
508 /// 566 fn resolve_imports(&mut self) -> ReachedFixedPoint {
509 /// This is a fix point algorithm. We resolve imports until no forward 567 let mut res = ReachedFixedPoint::Yes;
510 /// progress in resolving imports is made 568 let imports = std::mem::replace(&mut self.unresolved_imports, Vec::new());
511 fn resolve_imports(&mut self) { 569 let imports = imports
512 let mut n_previous_unresolved = self.unresolved_imports.len() + 1; 570 .into_iter()
513 571 .filter_map(|mut directive| {
514 while self.unresolved_imports.len() < n_previous_unresolved {
515 n_previous_unresolved = self.unresolved_imports.len();
516 let imports = std::mem::replace(&mut self.unresolved_imports, Vec::new());
517 for mut directive in imports {
518 directive.status = self.resolve_import(directive.module_id, &directive.import); 572 directive.status = self.resolve_import(directive.module_id, &directive.import);
519 match directive.status { 573 match directive.status {
520 PartialResolvedImport::Indeterminate(_) => { 574 PartialResolvedImport::Indeterminate(_) => {
521 self.record_resolved_import(&directive); 575 self.record_resolved_import(&directive);
522 // FIXME: For avoid performance regression, 576 // FIXME: For avoid performance regression,
523 // we consider an imported resolved if it is indeterminate (i.e not all namespace resolved) 577 // we consider an imported resolved if it is indeterminate (i.e not all namespace resolved)
524 self.resolved_imports.push(directive) 578 self.resolved_imports.push(directive);
579 res = ReachedFixedPoint::No;
580 None
525 } 581 }
526 PartialResolvedImport::Resolved(_) => { 582 PartialResolvedImport::Resolved(_) => {
527 self.record_resolved_import(&directive); 583 self.record_resolved_import(&directive);
528 self.resolved_imports.push(directive) 584 self.resolved_imports.push(directive);
529 } 585 res = ReachedFixedPoint::No;
530 PartialResolvedImport::Unresolved => { 586 None
531 self.unresolved_imports.push(directive);
532 } 587 }
588 PartialResolvedImport::Unresolved => Some(directive),
533 } 589 }
534 } 590 })
535 } 591 .collect();
592 self.unresolved_imports = imports;
593 res
536 } 594 }
537 595
538 fn resolve_import(&self, module_id: LocalModuleId, import: &Import) -> PartialResolvedImport { 596 fn resolve_import(&self, module_id: LocalModuleId, import: &Import) -> PartialResolvedImport {
@@ -856,6 +914,9 @@ impl DefCollector<'_> {
856 Err(UnresolvedMacro { .. }) => (), 914 Err(UnresolvedMacro { .. }) => (),
857 } 915 }
858 } 916 }
917 MacroDirectiveKind::Attr { .. } => {
918 // not yet :)
919 }
859 } 920 }
860 921
861 true 922 true
@@ -948,7 +1009,7 @@ impl DefCollector<'_> {
948 )); 1009 ));
949 } 1010 }
950 }, 1011 },
951 MacroDirectiveKind::Derive { .. } => { 1012 MacroDirectiveKind::Derive { .. } | MacroDirectiveKind::Attr { .. } => {
952 // FIXME: we might want to diagnose this too 1013 // FIXME: we might want to diagnose this too
953 } 1014 }
954 } 1015 }
@@ -1056,6 +1117,14 @@ impl ModCollector<'_, '_> {
1056 continue; 1117 continue;
1057 } 1118 }
1058 } 1119 }
1120
1121 if let Err(()) = self.resolve_attributes(&attrs, item) {
1122 // Do not process the item. It has at least one non-builtin attribute, which *must*
1123 // resolve to a proc macro (or fail to resolve), so we'll never see this item during
1124 // normal name resolution.
1125 continue;
1126 }
1127
1059 let module = self.def_collector.def_map.module_id(self.module_id); 1128 let module = self.def_collector.def_map.module_id(self.module_id);
1060 1129
1061 let mut def = None; 1130 let mut def = None;
@@ -1362,6 +1431,62 @@ impl ModCollector<'_, '_> {
1362 res 1431 res
1363 } 1432 }
1364 1433
1434 /// Resolves attributes on an item.
1435 ///
1436 /// Returns `Err` when some attributes could not be resolved to builtins and have been
1437 /// registered as unresolved.
1438 fn resolve_attributes(&mut self, attrs: &Attrs, mod_item: ModItem) -> Result<(), ()> {
1439 fn is_builtin_attr(path: &ModPath) -> bool {
1440 if path.kind == PathKind::Plain {
1441 if let Some(tool_module) = path.segments().first() {
1442 let tool_module = tool_module.to_string();
1443 if builtin_attr::TOOL_MODULES.iter().any(|m| tool_module == *m) {
1444 return true;
1445 }
1446 }
1447
1448 if let Some(name) = path.as_ident() {
1449 let name = name.to_string();
1450 if builtin_attr::INERT_ATTRIBUTES
1451 .iter()
1452 .chain(builtin_attr::EXTRA_ATTRIBUTES)
1453 .any(|attr| name == *attr)
1454 {
1455 return true;
1456 }
1457 }
1458 }
1459
1460 false
1461 }
1462
1463 // We failed to resolve an attribute on this item earlier, and are falling back to treating
1464 // the item as-is.
1465 if self.def_collector.ignore_attrs_on.contains(&mod_item) {
1466 return Ok(());
1467 }
1468
1469 match attrs.iter().find(|attr| !is_builtin_attr(&attr.path)) {
1470 Some(non_builtin_attr) => {
1471 log::debug!("non-builtin attribute {}", non_builtin_attr.path);
1472
1473 let ast_id = AstIdWithPath::new(
1474 self.file_id,
1475 mod_item.ast_id(self.item_tree),
1476 non_builtin_attr.path.as_ref().clone(),
1477 );
1478 self.def_collector.unexpanded_macros.push(MacroDirective {
1479 module_id: self.module_id,
1480 depth: self.macro_depth + 1,
1481 kind: MacroDirectiveKind::Attr { ast_id, attr: non_builtin_attr.id, mod_item },
1482 });
1483
1484 Err(())
1485 }
1486 None => Ok(()),
1487 }
1488 }
1489
1365 fn collect_derives(&mut self, attrs: &Attrs, ast_id: FileAstId<ast::Item>) { 1490 fn collect_derives(&mut self, attrs: &Attrs, ast_id: FileAstId<ast::Item>) {
1366 for derive in attrs.by_key("derive").attrs() { 1491 for derive in attrs.by_key("derive").attrs() {
1367 match derive.parse_derive() { 1492 match derive.parse_derive() {
@@ -1594,6 +1719,7 @@ mod tests {
1594 proc_macros: Default::default(), 1719 proc_macros: Default::default(),
1595 exports_proc_macros: false, 1720 exports_proc_macros: false,
1596 from_glob_import: Default::default(), 1721 from_glob_import: Default::default(),
1722 ignore_attrs_on: FxHashSet::default(),
1597 }; 1723 };
1598 collector.seed_with_top_level(); 1724 collector.seed_with_top_level();
1599 collector.collect(); 1725 collector.collect();