aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonas Schievink <[email protected]>2021-05-19 14:17:57 +0100
committerJonas Schievink <[email protected]>2021-05-19 14:17:57 +0100
commit3e186d47786899f7b4052f9d2cf060dbfe19e6f9 (patch)
tree695bb45a2202570fa1b20448a84b8b92139c2473
parent312f1fe20a6a0a8e69834c66f51b9abc9db5e0ce (diff)
internal: resolve attributes in name resolution
-rw-r--r--crates/hir_def/src/intern.rs8
-rw-r--r--crates/hir_def/src/nameres/collector.rs123
2 files changed, 128 insertions, 3 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 adfb78c94..c314b5309 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<'_> {
@@ -297,7 +301,10 @@ impl DefCollector<'_> {
297 self.resolve_imports(); 301 self.resolve_imports();
298 302
299 match self.resolve_macros() { 303 match self.resolve_macros() {
300 ReachedFixedPoint::Yes => break, 304 ReachedFixedPoint::Yes => match self.reseed_with_unresolved_attributes() {
305 ReachedFixedPoint::Yes => break,
306 ReachedFixedPoint::No => i += 1,
307 },
301 ReachedFixedPoint::No => i += 1, 308 ReachedFixedPoint::No => i += 1,
302 } 309 }
303 if i == FIXED_POINT_LIMIT { 310 if i == FIXED_POINT_LIMIT {
@@ -343,6 +350,50 @@ impl DefCollector<'_> {
343 } 350 }
344 } 351 }
345 352
353 /// When the fixed-point loop reaches a stable state, we might still have some unresolved
354 /// attributes (or unexpanded attribute proc macros) left over. This takes them, and feeds the
355 /// item they're applied to back into name resolution.
356 ///
357 /// This effectively ignores the fact that the macro is there and just treats the items as
358 /// normal code.
359 ///
360 /// This improves UX when proc macros are turned off or don't work, and replicates the behavior
361 /// before we supported proc. attribute macros.
362 fn reseed_with_unresolved_attributes(&mut self) -> ReachedFixedPoint {
363 let mut added_items = false;
364 let unexpanded_macros = std::mem::replace(&mut self.unexpanded_macros, Vec::new());
365 for directive in &unexpanded_macros {
366 if let MacroDirectiveKind::Attr { mod_item, .. } = &directive.kind {
367 // Make sure to only add such items once.
368 if !self.ignore_attrs_on.insert(*mod_item) {
369 continue;
370 }
371
372 let file_id = self.def_map[directive.module_id].definition_source(self.db).file_id;
373 let item_tree = self.db.file_item_tree(file_id);
374 let mod_dir = self.mod_dirs[&directive.module_id].clone();
375 ModCollector {
376 def_collector: &mut *self,
377 macro_depth: directive.depth,
378 module_id: directive.module_id,
379 file_id,
380 item_tree: &item_tree,
381 mod_dir,
382 }
383 .collect(&[*mod_item]);
384 added_items = true;
385 }
386 }
387 self.unexpanded_macros = unexpanded_macros;
388
389 if added_items {
390 // Continue name resolution with the new data.
391 ReachedFixedPoint::No
392 } else {
393 ReachedFixedPoint::Yes
394 }
395 }
396
346 /// Adds a definition of procedural macro `name` to the root module. 397 /// Adds a definition of procedural macro `name` to the root module.
347 /// 398 ///
348 /// # Notes on procedural macro resolution 399 /// # Notes on procedural macro resolution
@@ -849,6 +900,9 @@ impl DefCollector<'_> {
849 Err(UnresolvedMacro { .. }) => (), 900 Err(UnresolvedMacro { .. }) => (),
850 } 901 }
851 } 902 }
903 MacroDirectiveKind::Attr { .. } => {
904 // not yet :)
905 }
852 } 906 }
853 907
854 true 908 true
@@ -953,7 +1007,7 @@ impl DefCollector<'_> {
953 )); 1007 ));
954 } 1008 }
955 }, 1009 },
956 MacroDirectiveKind::Derive { .. } => { 1010 MacroDirectiveKind::Derive { .. } | MacroDirectiveKind::Attr { .. } => {
957 // FIXME: we might want to diagnose this too 1011 // FIXME: we might want to diagnose this too
958 } 1012 }
959 } 1013 }
@@ -1061,6 +1115,14 @@ impl ModCollector<'_, '_> {
1061 continue; 1115 continue;
1062 } 1116 }
1063 } 1117 }
1118
1119 if let Err(()) = self.resolve_attributes(&attrs, item) {
1120 // Do not process the item. It has at least one non-builtin attribute, which *must*
1121 // resolve to a proc macro (or fail to resolve), so we'll never see this item during
1122 // normal name resolution.
1123 continue;
1124 }
1125
1064 let module = self.def_collector.def_map.module_id(self.module_id); 1126 let module = self.def_collector.def_map.module_id(self.module_id);
1065 1127
1066 let mut def = None; 1128 let mut def = None;
@@ -1367,6 +1429,62 @@ impl ModCollector<'_, '_> {
1367 res 1429 res
1368 } 1430 }
1369 1431
1432 /// Resolves attributes on an item.
1433 ///
1434 /// Returns `Err` when some attributes could not be resolved to builtins and have been
1435 /// registered as unresolved.
1436 fn resolve_attributes(&mut self, attrs: &Attrs, mod_item: ModItem) -> Result<(), ()> {
1437 fn is_builtin_attr(path: &ModPath) -> bool {
1438 if path.kind == PathKind::Plain {
1439 if let Some(tool_module) = path.segments().first() {
1440 let tool_module = tool_module.to_string();
1441 if builtin_attr::TOOL_MODULES.iter().any(|m| tool_module == *m) {
1442 return true;
1443 }
1444 }
1445
1446 if let Some(name) = path.as_ident() {
1447 let name = name.to_string();
1448 if builtin_attr::INERT_ATTRIBUTES
1449 .iter()
1450 .chain(builtin_attr::EXTRA_ATTRIBUTES)
1451 .any(|attr| name == *attr)
1452 {
1453 return true;
1454 }
1455 }
1456 }
1457
1458 false
1459 }
1460
1461 // We failed to resolve an attribute on this item earlier, and are falling back to treating
1462 // the item as-is.
1463 if self.def_collector.ignore_attrs_on.contains(&mod_item) {
1464 return Ok(());
1465 }
1466
1467 match attrs.iter().find(|attr| !is_builtin_attr(&attr.path)) {
1468 Some(non_builtin_attr) => {
1469 log::debug!("non-builtin attribute {}", non_builtin_attr.path);
1470
1471 let ast_id = AstIdWithPath::new(
1472 self.file_id,
1473 mod_item.ast_id(self.item_tree),
1474 non_builtin_attr.path.as_ref().clone(),
1475 );
1476 self.def_collector.unexpanded_macros.push(MacroDirective {
1477 module_id: self.module_id,
1478 depth: self.macro_depth + 1,
1479 kind: MacroDirectiveKind::Attr { ast_id, attr: non_builtin_attr.id, mod_item },
1480 });
1481
1482 Err(())
1483 }
1484 None => Ok(()),
1485 }
1486 }
1487
1370 fn collect_derives(&mut self, attrs: &Attrs, ast_id: FileAstId<ast::Item>) { 1488 fn collect_derives(&mut self, attrs: &Attrs, ast_id: FileAstId<ast::Item>) {
1371 for derive in attrs.by_key("derive").attrs() { 1489 for derive in attrs.by_key("derive").attrs() {
1372 match derive.parse_derive() { 1490 match derive.parse_derive() {
@@ -1599,6 +1717,7 @@ mod tests {
1599 proc_macros: Default::default(), 1717 proc_macros: Default::default(),
1600 exports_proc_macros: false, 1718 exports_proc_macros: false,
1601 from_glob_import: Default::default(), 1719 from_glob_import: Default::default(),
1720 ignore_attrs_on: FxHashSet::default(),
1602 }; 1721 };
1603 collector.seed_with_top_level(); 1722 collector.seed_with_top_level();
1604 collector.collect(); 1723 collector.collect();