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/intern.rs8
-rw-r--r--crates/hir_def/src/nameres/collector.rs259
-rw-r--r--crates/hir_def/src/nameres/tests/macros.rs51
3 files changed, 254 insertions, 64 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 19db6cc59..3896be25d 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,
@@ -42,7 +43,7 @@ use crate::{
42 UnresolvedMacro, 43 UnresolvedMacro,
43}; 44};
44 45
45use super::proc_macro::ProcMacroDef; 46use super::proc_macro::{ProcMacroDef, ProcMacroKind};
46 47
47const GLOB_RECURSION_LIMIT: usize = 100; 48const GLOB_RECURSION_LIMIT: usize = 100;
48const EXPANSION_DEPTH_LIMIT: usize = 128; 49const EXPANSION_DEPTH_LIMIT: usize = 128;
@@ -99,6 +100,8 @@ 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(),
104 derive_helpers_in_scope: FxHashMap::default(),
102 }; 105 };
103 match block { 106 match block {
104 Some(block) => { 107 Some(block) => {
@@ -217,6 +220,7 @@ struct MacroDirective {
217enum MacroDirectiveKind { 220enum MacroDirectiveKind {
218 FnLike { ast_id: AstIdWithPath<ast::MacroCall>, fragment: FragmentKind }, 221 FnLike { ast_id: AstIdWithPath<ast::MacroCall>, fragment: FragmentKind },
219 Derive { ast_id: AstIdWithPath<ast::Item>, derive_attr: AttrId }, 222 Derive { ast_id: AstIdWithPath<ast::Item>, derive_attr: AttrId },
223 Attr { ast_id: AstIdWithPath<ast::Item>, attr: AttrId, mod_item: ModItem },
220} 224}
221 225
222struct DefData<'a> { 226struct DefData<'a> {
@@ -243,6 +247,10 @@ struct DefCollector<'a> {
243 proc_macros: Vec<(Name, ProcMacroExpander)>, 247 proc_macros: Vec<(Name, ProcMacroExpander)>,
244 exports_proc_macros: bool, 248 exports_proc_macros: bool,
245 from_glob_import: PerNsGlobImports, 249 from_glob_import: PerNsGlobImports,
250 ignore_attrs_on: FxHashSet<InFile<ModItem>>,
251 /// Tracks which custom derives are in scope for an item, to allow resolution of derive helper
252 /// attributes.
253 derive_helpers_in_scope: FxHashMap<AstId<ast::Item>, Vec<Name>>,
246} 254}
247 255
248impl DefCollector<'_> { 256impl DefCollector<'_> {
@@ -292,16 +300,26 @@ impl DefCollector<'_> {
292 fn collect(&mut self) { 300 fn collect(&mut self) {
293 // main name resolution fixed-point loop. 301 // main name resolution fixed-point loop.
294 let mut i = 0; 302 let mut i = 0;
295 loop { 303 'outer: loop {
296 self.db.check_canceled(); 304 loop {
297 self.resolve_imports(); 305 self.db.check_canceled();
306 loop {
307 if self.resolve_imports() == ReachedFixedPoint::Yes {
308 break;
309 }
310 }
311 if self.resolve_macros() == ReachedFixedPoint::Yes {
312 break;
313 }
298 314
299 match self.resolve_macros() { 315 i += 1;
300 ReachedFixedPoint::Yes => break, 316 if i == FIXED_POINT_LIMIT {
301 ReachedFixedPoint::No => i += 1, 317 log::error!("name resolution is stuck");
318 break 'outer;
319 }
302 } 320 }
303 if i == FIXED_POINT_LIMIT { 321
304 log::error!("name resolution is stuck"); 322 if self.reseed_with_unresolved_attributes() == ReachedFixedPoint::Yes {
305 break; 323 break;
306 } 324 }
307 } 325 }
@@ -343,6 +361,54 @@ impl DefCollector<'_> {
343 } 361 }
344 } 362 }
345 363
364 /// 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
366 /// item they're applied to back into name resolution.
367 ///
368 /// This effectively ignores the fact that the macro is there and just treats the items as
369 /// normal code.
370 ///
371 /// This improves UX when proc macros are turned off or don't work, and replicates the behavior
372 /// before we supported proc. attribute macros.
373 fn reseed_with_unresolved_attributes(&mut self) -> ReachedFixedPoint {
374 cov_mark::hit!(unresolved_attribute_fallback);
375
376 let mut added_items = false;
377 let unexpanded_macros = std::mem::replace(&mut self.unexpanded_macros, Vec::new());
378 for directive in &unexpanded_macros {
379 if let MacroDirectiveKind::Attr { ast_id, mod_item, .. } = &directive.kind {
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
385 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);
387 let mod_dir = self.mod_dirs[&directive.module_id].clone();
388 ModCollector {
389 def_collector: &mut *self,
390 macro_depth: directive.depth,
391 module_id: directive.module_id,
392 file_id,
393 item_tree: &item_tree,
394 mod_dir,
395 }
396 .collect(&[*mod_item]);
397 added_items = true;
398 }
399 }
400
401 // The collection above might add new unresolved macros (eg. derives), so merge the lists.
402 self.unexpanded_macros.extend(unexpanded_macros);
403
404 if added_items {
405 // Continue name resolution with the new data.
406 ReachedFixedPoint::No
407 } else {
408 ReachedFixedPoint::Yes
409 }
410 }
411
346 /// Adds a definition of procedural macro `name` to the root module. 412 /// Adds a definition of procedural macro `name` to the root module.
347 /// 413 ///
348 /// # Notes on procedural macro resolution 414 /// # Notes on procedural macro resolution
@@ -504,35 +570,35 @@ impl DefCollector<'_> {
504 } 570 }
505 } 571 }
506 572
507 /// Import resolution 573 /// Tries to resolve every currently unresolved import.
508 /// 574 fn resolve_imports(&mut self) -> ReachedFixedPoint {
509 /// This is a fix point algorithm. We resolve imports until no forward 575 let mut res = ReachedFixedPoint::Yes;
510 /// progress in resolving imports is made 576 let imports = std::mem::replace(&mut self.unresolved_imports, Vec::new());
511 fn resolve_imports(&mut self) { 577 let imports = imports
512 let mut n_previous_unresolved = self.unresolved_imports.len() + 1; 578 .into_iter()
513 579 .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); 580 directive.status = self.resolve_import(directive.module_id, &directive.import);
519 match directive.status { 581 match directive.status {
520 PartialResolvedImport::Indeterminate(_) => { 582 PartialResolvedImport::Indeterminate(_) => {
521 self.record_resolved_import(&directive); 583 self.record_resolved_import(&directive);
522 // FIXME: For avoid performance regression, 584 // FIXME: For avoid performance regression,
523 // we consider an imported resolved if it is indeterminate (i.e not all namespace resolved) 585 // we consider an imported resolved if it is indeterminate (i.e not all namespace resolved)
524 self.resolved_imports.push(directive) 586 self.resolved_imports.push(directive);
587 res = ReachedFixedPoint::No;
588 None
525 } 589 }
526 PartialResolvedImport::Resolved(_) => { 590 PartialResolvedImport::Resolved(_) => {
527 self.record_resolved_import(&directive); 591 self.record_resolved_import(&directive);
528 self.resolved_imports.push(directive) 592 self.resolved_imports.push(directive);
529 } 593 res = ReachedFixedPoint::No;
530 PartialResolvedImport::Unresolved => { 594 None
531 self.unresolved_imports.push(directive);
532 } 595 }
596 PartialResolvedImport::Unresolved => Some(directive),
533 } 597 }
534 } 598 })
535 } 599 .collect();
600 self.unresolved_imports = imports;
601 res
536 } 602 }
537 603
538 fn resolve_import(&self, module_id: LocalModuleId, import: &Import) -> PartialResolvedImport { 604 fn resolve_import(&self, module_id: LocalModuleId, import: &Import) -> PartialResolvedImport {
@@ -811,6 +877,17 @@ impl DefCollector<'_> {
811 let mut resolved = Vec::new(); 877 let mut resolved = Vec::new();
812 let mut res = ReachedFixedPoint::Yes; 878 let mut res = ReachedFixedPoint::Yes;
813 macros.retain(|directive| { 879 macros.retain(|directive| {
880 let resolver = |path| {
881 let resolved_res = self.def_map.resolve_path_fp_with_macro(
882 self.db,
883 ResolveMode::Other,
884 directive.module_id,
885 &path,
886 BuiltinShadowMode::Module,
887 );
888 resolved_res.resolved_def.take_macros()
889 };
890
814 match &directive.kind { 891 match &directive.kind {
815 MacroDirectiveKind::FnLike { ast_id, fragment } => { 892 MacroDirectiveKind::FnLike { ast_id, fragment } => {
816 match macro_call_as_call_id( 893 match macro_call_as_call_id(
@@ -818,16 +895,7 @@ impl DefCollector<'_> {
818 *fragment, 895 *fragment,
819 self.db, 896 self.db,
820 self.def_map.krate, 897 self.def_map.krate,
821 |path| { 898 &resolver,
822 let resolved_res = self.def_map.resolve_path_fp_with_macro(
823 self.db,
824 ResolveMode::Other,
825 directive.module_id,
826 &path,
827 BuiltinShadowMode::Module,
828 );
829 resolved_res.resolved_def.take_macros()
830 },
831 &mut |_err| (), 899 &mut |_err| (),
832 ) { 900 ) {
833 Ok(Ok(call_id)) => { 901 Ok(Ok(call_id)) => {
@@ -844,7 +912,7 @@ impl DefCollector<'_> {
844 *derive_attr, 912 *derive_attr,
845 self.db, 913 self.db,
846 self.def_map.krate, 914 self.def_map.krate,
847 |path| self.resolve_derive_macro(directive.module_id, &path), 915 &resolver,
848 ) { 916 ) {
849 Ok(call_id) => { 917 Ok(call_id) => {
850 resolved.push((directive.module_id, call_id, directive.depth)); 918 resolved.push((directive.module_id, call_id, directive.depth));
@@ -854,6 +922,9 @@ impl DefCollector<'_> {
854 Err(UnresolvedMacro { .. }) => (), 922 Err(UnresolvedMacro { .. }) => (),
855 } 923 }
856 } 924 }
925 MacroDirectiveKind::Attr { .. } => {
926 // not yet :)
927 }
857 } 928 }
858 929
859 true 930 true
@@ -867,18 +938,6 @@ impl DefCollector<'_> {
867 res 938 res
868 } 939 }
869 940
870 fn resolve_derive_macro(&self, module: LocalModuleId, path: &ModPath) -> Option<MacroDefId> {
871 let resolved_res = self.def_map.resolve_path_fp_with_macro(
872 self.db,
873 ResolveMode::Other,
874 module,
875 &path,
876 BuiltinShadowMode::Module,
877 );
878
879 resolved_res.resolved_def.take_macros()
880 }
881
882 fn collect_macro_expansion( 941 fn collect_macro_expansion(
883 &mut self, 942 &mut self,
884 module_id: LocalModuleId, 943 module_id: LocalModuleId,
@@ -895,22 +954,33 @@ impl DefCollector<'_> {
895 // First, fetch the raw expansion result for purposes of error reporting. This goes through 954 // First, fetch the raw expansion result for purposes of error reporting. This goes through
896 // `macro_expand_error` to avoid depending on the full expansion result (to improve 955 // `macro_expand_error` to avoid depending on the full expansion result (to improve
897 // incrementality). 956 // incrementality).
957 let loc: MacroCallLoc = self.db.lookup_intern_macro(macro_call_id);
898 let err = self.db.macro_expand_error(macro_call_id); 958 let err = self.db.macro_expand_error(macro_call_id);
899 if let Some(err) = err { 959 if let Some(err) = err {
900 if let MacroCallId::LazyMacro(id) = macro_call_id { 960 let diag = match err {
901 let loc: MacroCallLoc = self.db.lookup_intern_macro(id); 961 hir_expand::ExpandError::UnresolvedProcMacro => {
962 // Missing proc macros are non-fatal, so they are handled specially.
963 DefDiagnostic::unresolved_proc_macro(module_id, loc.kind.clone())
964 }
965 _ => DefDiagnostic::macro_error(module_id, loc.kind.clone(), err.to_string()),
966 };
902 967
903 let diag = match err { 968 self.def_map.diagnostics.push(diag);
904 hir_expand::ExpandError::UnresolvedProcMacro => { 969 }
905 // Missing proc macros are non-fatal, so they are handled specially.
906 DefDiagnostic::unresolved_proc_macro(module_id, loc.kind)
907 }
908 _ => DefDiagnostic::macro_error(module_id, loc.kind, err.to_string()),
909 };
910 970
911 self.def_map.diagnostics.push(diag); 971 // If we've just resolved a derive, record its helper attributes.
972 if let MacroCallKind::Derive { ast_id, .. } = &loc.kind {
973 if loc.def.krate != self.def_map.krate {
974 let def_map = self.db.crate_def_map(loc.def.krate);
975 if let Some(def) = def_map.exported_proc_macros.get(&loc.def) {
976 if let ProcMacroKind::CustomDerive { helpers } = &def.kind {
977 self.derive_helpers_in_scope
978 .entry(*ast_id)
979 .or_default()
980 .extend(helpers.iter().cloned());
981 }
982 }
912 } 983 }
913 // FIXME: Handle eager macros.
914 } 984 }
915 985
916 // Then, fetch and process the item tree. This will reuse the expansion result from above. 986 // Then, fetch and process the item tree. This will reuse the expansion result from above.
@@ -958,7 +1028,7 @@ impl DefCollector<'_> {
958 )); 1028 ));
959 } 1029 }
960 }, 1030 },
961 MacroDirectiveKind::Derive { .. } => { 1031 MacroDirectiveKind::Derive { .. } | MacroDirectiveKind::Attr { .. } => {
962 // FIXME: we might want to diagnose this too 1032 // FIXME: we might want to diagnose this too
963 } 1033 }
964 } 1034 }
@@ -1066,6 +1136,13 @@ impl ModCollector<'_, '_> {
1066 continue; 1136 continue;
1067 } 1137 }
1068 } 1138 }
1139
1140 if let Err(()) = self.resolve_attributes(&attrs, item) {
1141 // Do not process the item. It has at least one non-builtin attribute, so the
1142 // fixed-point algorithm is required to resolve the rest of them.
1143 continue;
1144 }
1145
1069 let module = self.def_collector.def_map.module_id(self.module_id); 1146 let module = self.def_collector.def_map.module_id(self.module_id);
1070 1147
1071 let mut def = None; 1148 let mut def = None;
@@ -1372,6 +1449,62 @@ impl ModCollector<'_, '_> {
1372 res 1449 res
1373 } 1450 }
1374 1451
1452 /// Resolves attributes on an item.
1453 ///
1454 /// Returns `Err` when some attributes could not be resolved to builtins and have been
1455 /// registered as unresolved.
1456 fn resolve_attributes(&mut self, attrs: &Attrs, mod_item: ModItem) -> Result<(), ()> {
1457 fn is_builtin_attr(path: &ModPath) -> bool {
1458 if path.kind == PathKind::Plain {
1459 if let Some(tool_module) = path.segments().first() {
1460 let tool_module = tool_module.to_string();
1461 if builtin_attr::TOOL_MODULES.iter().any(|m| tool_module == *m) {
1462 return true;
1463 }
1464 }
1465
1466 if let Some(name) = path.as_ident() {
1467 let name = name.to_string();
1468 if builtin_attr::INERT_ATTRIBUTES
1469 .iter()
1470 .chain(builtin_attr::EXTRA_ATTRIBUTES)
1471 .any(|attr| name == *attr)
1472 {
1473 return true;
1474 }
1475 }
1476 }
1477
1478 false
1479 }
1480
1481 // We failed to resolve an attribute on this item earlier, and are falling back to treating
1482 // the item as-is.
1483 if self.def_collector.ignore_attrs_on.contains(&InFile::new(self.file_id, mod_item)) {
1484 return Ok(());
1485 }
1486
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
1491 let ast_id = AstIdWithPath::new(
1492 self.file_id,
1493 mod_item.ast_id(self.item_tree),
1494 non_builtin_attr.path.as_ref().clone(),
1495 );
1496 self.def_collector.unexpanded_macros.push(MacroDirective {
1497 module_id: self.module_id,
1498 depth: self.macro_depth + 1,
1499 kind: MacroDirectiveKind::Attr { ast_id, attr: non_builtin_attr.id, mod_item },
1500 });
1501
1502 Err(())
1503 }
1504 None => Ok(()),
1505 }
1506 }
1507
1375 fn collect_derives(&mut self, attrs: &Attrs, ast_id: FileAstId<ast::Item>) { 1508 fn collect_derives(&mut self, attrs: &Attrs, ast_id: FileAstId<ast::Item>) {
1376 for derive in attrs.by_key("derive").attrs() { 1509 for derive in attrs.by_key("derive").attrs() {
1377 match derive.parse_derive() { 1510 match derive.parse_derive() {
@@ -1604,6 +1737,8 @@ mod tests {
1604 proc_macros: Default::default(), 1737 proc_macros: Default::default(),
1605 exports_proc_macros: false, 1738 exports_proc_macros: false,
1606 from_glob_import: Default::default(), 1739 from_glob_import: Default::default(),
1740 ignore_attrs_on: FxHashSet::default(),
1741 derive_helpers_in_scope: FxHashMap::default(),
1607 }; 1742 };
1608 collector.seed_with_top_level(); 1743 collector.seed_with_top_level();
1609 collector.collect(); 1744 collector.collect();
diff --git a/crates/hir_def/src/nameres/tests/macros.rs b/crates/hir_def/src/nameres/tests/macros.rs
index c37f915ab..6eb5f97be 100644
--- a/crates/hir_def/src/nameres/tests/macros.rs
+++ b/crates/hir_def/src/nameres/tests/macros.rs
@@ -686,6 +686,56 @@ pub trait Clone {}
686} 686}
687 687
688#[test] 688#[test]
689fn builtin_derive_with_unresolved_attributes_fall_back() {
690 // Tests that we still resolve derives after ignoring an unresolved attribute.
691 cov_mark::check!(unresolved_attribute_fallback);
692 let map = compute_crate_def_map(
693 r#"
694 //- /main.rs crate:main deps:core
695 use core::Clone;
696
697 #[derive(Clone)]
698 #[unresolved]
699 struct Foo;
700
701 //- /core.rs crate:core
702 #[rustc_builtin_macro]
703 pub macro Clone {}
704 "#,
705 );
706 assert_eq!(map.modules[map.root].scope.impls().len(), 1);
707}
708
709#[test]
710fn unresolved_attributes_fall_back_track_per_file_moditems() {
711 // Tests that we track per-file ModItems when ignoring an unresolved attribute.
712 // Just tracking the `ModItem` leads to `Foo` getting ignored.
713
714 check(
715 r#"
716 //- /main.rs crate:main
717
718 mod submod;
719
720 #[unresolved]
721 struct Foo;
722
723 //- /submod.rs
724 #[unresolved]
725 struct Bar;
726 "#,
727 expect![[r#"
728 crate
729 Foo: t v
730 submod: t
731
732 crate::submod
733 Bar: t v
734 "#]],
735 );
736}
737
738#[test]
689fn macro_expansion_overflow() { 739fn macro_expansion_overflow() {
690 cov_mark::check!(macro_expansion_overflow); 740 cov_mark::check!(macro_expansion_overflow);
691 check( 741 check(
@@ -842,7 +892,6 @@ fn collects_derive_helpers() {
842fn resolve_macro_def() { 892fn resolve_macro_def() {
843 check( 893 check(
844 r#" 894 r#"
845//- /lib.rs
846pub macro structs($($i:ident),*) { 895pub macro structs($($i:ident),*) {
847 $(struct $i { field: u32 } )* 896 $(struct $i { field: u32 } )*
848} 897}