diff options
author | Jonas Schievink <[email protected]> | 2021-05-31 12:37:11 +0100 |
---|---|---|
committer | Jonas Schievink <[email protected]> | 2021-06-03 17:09:21 +0100 |
commit | e5a2c6596ddd11b0d57042224ac7c1d7691ec33b (patch) | |
tree | f0476ad40103b5d3dea60f81fca32c63fe9618d7 | |
parent | 7f9c4a59d9a84cd8c734286937439b5cd215be27 (diff) |
Expand procedural attribute macros
-rw-r--r-- | crates/hir/src/lib.rs | 16 | ||||
-rw-r--r-- | crates/hir_def/src/lib.rs | 40 | ||||
-rw-r--r-- | crates/hir_def/src/nameres/collector.rs | 56 | ||||
-rw-r--r-- | crates/hir_expand/src/db.rs | 11 | ||||
-rw-r--r-- | crates/hir_expand/src/input.rs | 19 | ||||
-rw-r--r-- | crates/hir_expand/src/lib.rs | 24 | ||||
-rw-r--r-- | crates/hir_expand/src/proc_macro.rs | 7 |
7 files changed, 159 insertions, 14 deletions
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index d3ef29db4..b43d61d0e 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs | |||
@@ -534,6 +534,18 @@ impl Module { | |||
534 | Some(derive_name.clone()), | 534 | Some(derive_name.clone()), |
535 | ) | 535 | ) |
536 | } | 536 | } |
537 | MacroCallKind::Attr { ast_id, invoc_attr_index, attr_name, .. } => { | ||
538 | let node = ast_id.to_node(db.upcast()); | ||
539 | let attr = | ||
540 | node.attrs().nth((*invoc_attr_index) as usize).unwrap_or_else( | ||
541 | || panic!("cannot find attribute #{}", invoc_attr_index), | ||
542 | ); | ||
543 | ( | ||
544 | ast_id.file_id, | ||
545 | SyntaxNodePtr::from(AstPtr::new(&attr)), | ||
546 | Some(attr_name.clone()), | ||
547 | ) | ||
548 | } | ||
537 | }; | 549 | }; |
538 | sink.push(UnresolvedProcMacro { | 550 | sink.push(UnresolvedProcMacro { |
539 | file, | 551 | file, |
@@ -558,7 +570,9 @@ impl Module { | |||
558 | let node = ast_id.to_node(db.upcast()); | 570 | let node = ast_id.to_node(db.upcast()); |
559 | (ast_id.file_id, SyntaxNodePtr::from(AstPtr::new(&node))) | 571 | (ast_id.file_id, SyntaxNodePtr::from(AstPtr::new(&node))) |
560 | } | 572 | } |
561 | MacroCallKind::Derive { ast_id, .. } => { | 573 | MacroCallKind::Derive { ast_id, .. } |
574 | | MacroCallKind::Attr { ast_id, .. } => { | ||
575 | // FIXME: point to the attribute instead, this creates very large diagnostics | ||
562 | let node = ast_id.to_node(db.upcast()); | 576 | let node = ast_id.to_node(db.upcast()); |
563 | (ast_id.file_id, SyntaxNodePtr::from(AstPtr::new(&node))) | 577 | (ast_id.file_id, SyntaxNodePtr::from(AstPtr::new(&node))) |
564 | } | 578 | } |
diff --git a/crates/hir_def/src/lib.rs b/crates/hir_def/src/lib.rs index 9aa95720a..987485acc 100644 --- a/crates/hir_def/src/lib.rs +++ b/crates/hir_def/src/lib.rs | |||
@@ -55,6 +55,7 @@ use std::{ | |||
55 | sync::Arc, | 55 | sync::Arc, |
56 | }; | 56 | }; |
57 | 57 | ||
58 | use attr::Attr; | ||
58 | use base_db::{impl_intern_key, salsa, CrateId}; | 59 | use base_db::{impl_intern_key, salsa, CrateId}; |
59 | use hir_expand::{ | 60 | use hir_expand::{ |
60 | ast_id_map::FileAstId, | 61 | ast_id_map::FileAstId, |
@@ -768,3 +769,42 @@ fn derive_macro_as_call_id( | |||
768 | .into(); | 769 | .into(); |
769 | Ok(res) | 770 | Ok(res) |
770 | } | 771 | } |
772 | |||
773 | fn attr_macro_as_call_id( | ||
774 | item_attr: &AstIdWithPath<ast::Item>, | ||
775 | macro_attr: &Attr, | ||
776 | db: &dyn db::DefDatabase, | ||
777 | krate: CrateId, | ||
778 | resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, | ||
779 | ) -> Result<MacroCallId, UnresolvedMacro> { | ||
780 | let def: MacroDefId = resolver(item_attr.path.clone()) | ||
781 | .ok_or_else(|| UnresolvedMacro { path: item_attr.path.clone() })?; | ||
782 | let last_segment = item_attr | ||
783 | .path | ||
784 | .segments() | ||
785 | .last() | ||
786 | .ok_or_else(|| UnresolvedMacro { path: item_attr.path.clone() })?; | ||
787 | let mut arg = match ¯o_attr.input { | ||
788 | Some(input) => match &**input { | ||
789 | attr::AttrInput::Literal(_) => tt::Subtree::default(), | ||
790 | attr::AttrInput::TokenTree(tt) => tt.clone(), | ||
791 | }, | ||
792 | None => tt::Subtree::default(), | ||
793 | }; | ||
794 | // The parentheses are always disposed here. | ||
795 | arg.delimiter = None; | ||
796 | |||
797 | let res = def | ||
798 | .as_lazy_macro( | ||
799 | db.upcast(), | ||
800 | krate, | ||
801 | MacroCallKind::Attr { | ||
802 | ast_id: item_attr.ast_id, | ||
803 | attr_name: last_segment.to_string(), | ||
804 | attr_args: arg, | ||
805 | invoc_attr_index: macro_attr.id.ast_index, | ||
806 | }, | ||
807 | ) | ||
808 | .into(); | ||
809 | Ok(res) | ||
810 | } | ||
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs index 6b41921ae..874a4ebb1 100644 --- a/crates/hir_def/src/nameres/collector.rs +++ b/crates/hir_def/src/nameres/collector.rs | |||
@@ -23,7 +23,7 @@ use syntax::ast; | |||
23 | 23 | ||
24 | use crate::{ | 24 | use crate::{ |
25 | attr::{Attr, AttrId, AttrInput, Attrs}, | 25 | attr::{Attr, AttrId, AttrInput, Attrs}, |
26 | builtin_attr, | 26 | attr_macro_as_call_id, builtin_attr, |
27 | db::DefDatabase, | 27 | db::DefDatabase, |
28 | derive_macro_as_call_id, | 28 | derive_macro_as_call_id, |
29 | intern::Interned, | 29 | intern::Interned, |
@@ -223,7 +223,7 @@ struct MacroDirective { | |||
223 | enum MacroDirectiveKind { | 223 | enum MacroDirectiveKind { |
224 | FnLike { ast_id: AstIdWithPath<ast::MacroCall>, fragment: FragmentKind }, | 224 | FnLike { ast_id: AstIdWithPath<ast::MacroCall>, fragment: FragmentKind }, |
225 | Derive { ast_id: AstIdWithPath<ast::Item>, derive_attr: AttrId }, | 225 | Derive { ast_id: AstIdWithPath<ast::Item>, derive_attr: AttrId }, |
226 | Attr { ast_id: AstIdWithPath<ast::Item>, attr: AttrId, mod_item: ModItem }, | 226 | Attr { ast_id: AstIdWithPath<ast::Item>, attr: Attr, mod_item: ModItem }, |
227 | } | 227 | } |
228 | 228 | ||
229 | struct DefData<'a> { | 229 | struct DefData<'a> { |
@@ -419,7 +419,7 @@ impl DefCollector<'_> { | |||
419 | let mut unresolved_macros = std::mem::replace(&mut self.unresolved_macros, Vec::new()); | 419 | let mut unresolved_macros = std::mem::replace(&mut self.unresolved_macros, Vec::new()); |
420 | let pos = unresolved_macros.iter().position(|directive| { | 420 | let pos = unresolved_macros.iter().position(|directive| { |
421 | if let MacroDirectiveKind::Attr { ast_id, mod_item, attr } = &directive.kind { | 421 | if let MacroDirectiveKind::Attr { ast_id, mod_item, attr } = &directive.kind { |
422 | self.skip_attrs.insert(ast_id.ast_id.with_value(*mod_item), *attr); | 422 | self.skip_attrs.insert(ast_id.ast_id.with_value(*mod_item), attr.id); |
423 | 423 | ||
424 | let file_id = ast_id.ast_id.file_id; | 424 | let file_id = ast_id.ast_id.file_id; |
425 | let item_tree = self.db.file_item_tree(file_id); | 425 | let item_tree = self.db.file_item_tree(file_id); |
@@ -1050,7 +1050,7 @@ impl DefCollector<'_> { | |||
1050 | let file_id = ast_id.ast_id.file_id; | 1050 | let file_id = ast_id.ast_id.file_id; |
1051 | let item_tree = self.db.file_item_tree(file_id); | 1051 | let item_tree = self.db.file_item_tree(file_id); |
1052 | let mod_dir = self.mod_dirs[&directive.module_id].clone(); | 1052 | let mod_dir = self.mod_dirs[&directive.module_id].clone(); |
1053 | self.skip_attrs.insert(InFile::new(file_id, *mod_item), *attr); | 1053 | self.skip_attrs.insert(InFile::new(file_id, *mod_item), attr.id); |
1054 | ModCollector { | 1054 | ModCollector { |
1055 | def_collector: &mut *self, | 1055 | def_collector: &mut *self, |
1056 | macro_depth: directive.depth, | 1056 | macro_depth: directive.depth, |
@@ -1068,7 +1068,51 @@ impl DefCollector<'_> { | |||
1068 | } | 1068 | } |
1069 | 1069 | ||
1070 | // Not resolved to a derive helper, so try to resolve as a macro. | 1070 | // Not resolved to a derive helper, so try to resolve as a macro. |
1071 | // FIXME: not yet :) | 1071 | match attr_macro_as_call_id( |
1072 | ast_id, | ||
1073 | attr, | ||
1074 | self.db, | ||
1075 | self.def_map.krate, | ||
1076 | &resolver, | ||
1077 | ) { | ||
1078 | Ok(call_id) => { | ||
1079 | let loc: MacroCallLoc = self.db.lookup_intern_macro(call_id); | ||
1080 | if let MacroDefKind::ProcMacro(exp, ..) = &loc.def.kind { | ||
1081 | if exp.is_dummy() { | ||
1082 | // Proc macros that cannot be expanded are treated as not | ||
1083 | // resolved, in order to fall back later. | ||
1084 | self.def_map.diagnostics.push( | ||
1085 | DefDiagnostic::unresolved_proc_macro( | ||
1086 | directive.module_id, | ||
1087 | loc.kind, | ||
1088 | ), | ||
1089 | ); | ||
1090 | |||
1091 | let file_id = ast_id.ast_id.file_id; | ||
1092 | let item_tree = self.db.file_item_tree(file_id); | ||
1093 | let mod_dir = self.mod_dirs[&directive.module_id].clone(); | ||
1094 | self.skip_attrs | ||
1095 | .insert(InFile::new(file_id, *mod_item), attr.id); | ||
1096 | ModCollector { | ||
1097 | def_collector: &mut *self, | ||
1098 | macro_depth: directive.depth, | ||
1099 | module_id: directive.module_id, | ||
1100 | file_id, | ||
1101 | item_tree: &item_tree, | ||
1102 | mod_dir, | ||
1103 | } | ||
1104 | .collect(&[*mod_item]); | ||
1105 | |||
1106 | // Remove the macro directive. | ||
1107 | return false; | ||
1108 | } | ||
1109 | } | ||
1110 | resolved.push((directive.module_id, call_id, directive.depth)); | ||
1111 | res = ReachedFixedPoint::No; | ||
1112 | return false; | ||
1113 | } | ||
1114 | Err(UnresolvedMacro { .. }) => (), | ||
1115 | } | ||
1072 | } | 1116 | } |
1073 | } | 1117 | } |
1074 | 1118 | ||
@@ -1628,7 +1672,7 @@ impl ModCollector<'_, '_> { | |||
1628 | self.def_collector.unresolved_macros.push(MacroDirective { | 1672 | self.def_collector.unresolved_macros.push(MacroDirective { |
1629 | module_id: self.module_id, | 1673 | module_id: self.module_id, |
1630 | depth: self.macro_depth + 1, | 1674 | depth: self.macro_depth + 1, |
1631 | kind: MacroDirectiveKind::Attr { ast_id, attr: attr.id, mod_item }, | 1675 | kind: MacroDirectiveKind::Attr { ast_id, attr: attr.clone(), mod_item }, |
1632 | }); | 1676 | }); |
1633 | 1677 | ||
1634 | return Err(()); | 1678 | return Err(()); |
diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs index e8f4af309..3ebe194e4 100644 --- a/crates/hir_expand/src/db.rs +++ b/crates/hir_expand/src/db.rs | |||
@@ -13,8 +13,8 @@ use syntax::{ | |||
13 | 13 | ||
14 | use crate::{ | 14 | use crate::{ |
15 | ast_id_map::AstIdMap, hygiene::HygieneFrame, input::process_macro_input, BuiltinDeriveExpander, | 15 | ast_id_map::AstIdMap, hygiene::HygieneFrame, input::process_macro_input, BuiltinDeriveExpander, |
16 | BuiltinFnLikeExpander, HirFileId, HirFileIdRepr, MacroCallId, MacroCallLoc, MacroDefId, | 16 | BuiltinFnLikeExpander, HirFileId, HirFileIdRepr, MacroCallId, MacroCallKind, MacroCallLoc, |
17 | MacroDefKind, MacroFile, ProcMacroExpander, | 17 | MacroDefId, MacroDefKind, MacroFile, ProcMacroExpander, |
18 | }; | 18 | }; |
19 | 19 | ||
20 | /// Total limit on the number of tokens produced by any macro invocation. | 20 | /// Total limit on the number of tokens produced by any macro invocation. |
@@ -377,7 +377,12 @@ fn expand_proc_macro( | |||
377 | _ => unreachable!(), | 377 | _ => unreachable!(), |
378 | }; | 378 | }; |
379 | 379 | ||
380 | expander.expand(db, loc.krate, ¯o_arg.0) | 380 | let attr_arg = match &loc.kind { |
381 | MacroCallKind::Attr { attr_args, .. } => Some(attr_args), | ||
382 | _ => None, | ||
383 | }; | ||
384 | |||
385 | expander.expand(db, loc.krate, ¯o_arg.0, attr_arg) | ||
381 | } | 386 | } |
382 | 387 | ||
383 | fn is_self_replicating(from: &SyntaxNode, to: &SyntaxNode) -> bool { | 388 | fn is_self_replicating(from: &SyntaxNode, to: &SyntaxNode) -> bool { |
diff --git a/crates/hir_expand/src/input.rs b/crates/hir_expand/src/input.rs index fe4790e7b..40116a479 100644 --- a/crates/hir_expand/src/input.rs +++ b/crates/hir_expand/src/input.rs | |||
@@ -28,6 +28,14 @@ pub(crate) fn process_macro_input( | |||
28 | 28 | ||
29 | remove_derives_up_to(item, derive_attr_index as usize).syntax().clone() | 29 | remove_derives_up_to(item, derive_attr_index as usize).syntax().clone() |
30 | } | 30 | } |
31 | MacroCallKind::Attr { invoc_attr_index, .. } => { | ||
32 | let item = match ast::Item::cast(node.clone()) { | ||
33 | Some(item) => item, | ||
34 | None => return node, | ||
35 | }; | ||
36 | |||
37 | remove_attr_invoc(item, invoc_attr_index as usize).syntax().clone() | ||
38 | } | ||
31 | } | 39 | } |
32 | } | 40 | } |
33 | 41 | ||
@@ -46,6 +54,17 @@ fn remove_derives_up_to(item: ast::Item, attr_index: usize) -> ast::Item { | |||
46 | item | 54 | item |
47 | } | 55 | } |
48 | 56 | ||
57 | /// Removes the attribute invoking an attribute macro from `item`. | ||
58 | fn remove_attr_invoc(item: ast::Item, attr_index: usize) -> ast::Item { | ||
59 | let item = item.clone_for_update(); | ||
60 | let attr = item | ||
61 | .attrs() | ||
62 | .nth(attr_index) | ||
63 | .unwrap_or_else(|| panic!("cannot find attribute #{}", attr_index)); | ||
64 | attr.syntax().detach(); | ||
65 | item | ||
66 | } | ||
67 | |||
49 | #[cfg(test)] | 68 | #[cfg(test)] |
50 | mod tests { | 69 | mod tests { |
51 | use base_db::fixture::WithFixture; | 70 | use base_db::fixture::WithFixture; |
diff --git a/crates/hir_expand/src/lib.rs b/crates/hir_expand/src/lib.rs index 90d8ae240..618f26b95 100644 --- a/crates/hir_expand/src/lib.rs +++ b/crates/hir_expand/src/lib.rs | |||
@@ -258,14 +258,29 @@ pub enum MacroCallKind { | |||
258 | /// out-of-line modules, which may have attributes spread across 2 files! | 258 | /// out-of-line modules, which may have attributes spread across 2 files! |
259 | derive_attr_index: u32, | 259 | derive_attr_index: u32, |
260 | }, | 260 | }, |
261 | Attr { | ||
262 | ast_id: AstId<ast::Item>, | ||
263 | attr_name: String, | ||
264 | attr_args: tt::Subtree, | ||
265 | /// Syntactical index of the invoking `#[attribute]`. | ||
266 | /// | ||
267 | /// Outer attributes are counted first, then inner attributes. This does not support | ||
268 | /// out-of-line modules, which may have attributes spread across 2 files! | ||
269 | invoc_attr_index: u32, | ||
270 | }, | ||
261 | } | 271 | } |
262 | 272 | ||
273 | // FIXME: attribute indices do not account for `cfg_attr`, which means that we'll strip the whole | ||
274 | // `cfg_attr` instead of just one of the attributes it expands to | ||
275 | |||
263 | impl MacroCallKind { | 276 | impl MacroCallKind { |
264 | /// Returns the file containing the macro invocation. | 277 | /// Returns the file containing the macro invocation. |
265 | fn file_id(&self) -> HirFileId { | 278 | fn file_id(&self) -> HirFileId { |
266 | match self { | 279 | match self { |
267 | MacroCallKind::FnLike { ast_id, .. } => ast_id.file_id, | 280 | MacroCallKind::FnLike { ast_id, .. } => ast_id.file_id, |
268 | MacroCallKind::Derive { ast_id, .. } => ast_id.file_id, | 281 | MacroCallKind::Derive { ast_id, .. } | MacroCallKind::Attr { ast_id, .. } => { |
282 | ast_id.file_id | ||
283 | } | ||
269 | } | 284 | } |
270 | } | 285 | } |
271 | 286 | ||
@@ -274,7 +289,7 @@ impl MacroCallKind { | |||
274 | MacroCallKind::FnLike { ast_id, .. } => { | 289 | MacroCallKind::FnLike { ast_id, .. } => { |
275 | ast_id.with_value(ast_id.to_node(db).syntax().clone()) | 290 | ast_id.with_value(ast_id.to_node(db).syntax().clone()) |
276 | } | 291 | } |
277 | MacroCallKind::Derive { ast_id, .. } => { | 292 | MacroCallKind::Derive { ast_id, .. } | MacroCallKind::Attr { ast_id, .. } => { |
278 | ast_id.with_value(ast_id.to_node(db).syntax().clone()) | 293 | ast_id.with_value(ast_id.to_node(db).syntax().clone()) |
279 | } | 294 | } |
280 | } | 295 | } |
@@ -285,7 +300,9 @@ impl MacroCallKind { | |||
285 | MacroCallKind::FnLike { ast_id, .. } => { | 300 | MacroCallKind::FnLike { ast_id, .. } => { |
286 | Some(ast_id.to_node(db).token_tree()?.syntax().clone()) | 301 | Some(ast_id.to_node(db).token_tree()?.syntax().clone()) |
287 | } | 302 | } |
288 | MacroCallKind::Derive { ast_id, .. } => Some(ast_id.to_node(db).syntax().clone()), | 303 | MacroCallKind::Derive { ast_id, .. } | MacroCallKind::Attr { ast_id, .. } => { |
304 | Some(ast_id.to_node(db).syntax().clone()) | ||
305 | } | ||
289 | } | 306 | } |
290 | } | 307 | } |
291 | 308 | ||
@@ -293,6 +310,7 @@ impl MacroCallKind { | |||
293 | match self { | 310 | match self { |
294 | MacroCallKind::FnLike { fragment, .. } => *fragment, | 311 | MacroCallKind::FnLike { fragment, .. } => *fragment, |
295 | MacroCallKind::Derive { .. } => FragmentKind::Items, | 312 | MacroCallKind::Derive { .. } => FragmentKind::Items, |
313 | MacroCallKind::Attr { .. } => FragmentKind::Items, // is this always correct? | ||
296 | } | 314 | } |
297 | } | 315 | } |
298 | } | 316 | } |
diff --git a/crates/hir_expand/src/proc_macro.rs b/crates/hir_expand/src/proc_macro.rs index d5643393a..dbe1b446e 100644 --- a/crates/hir_expand/src/proc_macro.rs +++ b/crates/hir_expand/src/proc_macro.rs | |||
@@ -28,11 +28,16 @@ impl ProcMacroExpander { | |||
28 | Self { krate, proc_macro_id: None } | 28 | Self { krate, proc_macro_id: None } |
29 | } | 29 | } |
30 | 30 | ||
31 | pub fn is_dummy(&self) -> bool { | ||
32 | self.proc_macro_id.is_none() | ||
33 | } | ||
34 | |||
31 | pub fn expand( | 35 | pub fn expand( |
32 | self, | 36 | self, |
33 | db: &dyn AstDatabase, | 37 | db: &dyn AstDatabase, |
34 | calling_crate: CrateId, | 38 | calling_crate: CrateId, |
35 | tt: &tt::Subtree, | 39 | tt: &tt::Subtree, |
40 | attr_arg: Option<&tt::Subtree>, | ||
36 | ) -> Result<tt::Subtree, mbe::ExpandError> { | 41 | ) -> Result<tt::Subtree, mbe::ExpandError> { |
37 | match self.proc_macro_id { | 42 | match self.proc_macro_id { |
38 | Some(id) => { | 43 | Some(id) => { |
@@ -46,7 +51,7 @@ impl ProcMacroExpander { | |||
46 | // Proc macros have access to the environment variables of the invoking crate. | 51 | // Proc macros have access to the environment variables of the invoking crate. |
47 | let env = &krate_graph[calling_crate].env; | 52 | let env = &krate_graph[calling_crate].env; |
48 | 53 | ||
49 | proc_macro.expander.expand(&tt, None, &env).map_err(mbe::ExpandError::from) | 54 | proc_macro.expander.expand(&tt, attr_arg, &env).map_err(mbe::ExpandError::from) |
50 | } | 55 | } |
51 | None => Err(mbe::ExpandError::UnresolvedProcMacro), | 56 | None => Err(mbe::ExpandError::UnresolvedProcMacro), |
52 | } | 57 | } |