diff options
author | Jonas Schievink <[email protected]> | 2021-05-19 14:17:57 +0100 |
---|---|---|
committer | Jonas Schievink <[email protected]> | 2021-05-19 14:17:57 +0100 |
commit | 3e186d47786899f7b4052f9d2cf060dbfe19e6f9 (patch) | |
tree | 695bb45a2202570fa1b20448a84b8b92139c2473 | |
parent | 312f1fe20a6a0a8e69834c66f51b9abc9db5e0ce (diff) |
internal: resolve attributes in name resolution
-rw-r--r-- | crates/hir_def/src/intern.rs | 8 | ||||
-rw-r--r-- | crates/hir_def/src/nameres/collector.rs | 123 |
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 | ||
5 | use std::{ | 5 | use 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 | ||
174 | impl<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 | |||
174 | pub struct InternStorage<T: ?Sized> { | 180 | pub 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 | ||
22 | use crate::{ | 22 | use 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 { | |||
217 | enum MacroDirectiveKind { | 219 | enum 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 | ||
222 | struct DefData<'a> { | 225 | struct 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 | ||
248 | impl DefCollector<'_> { | 252 | impl 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(); |