aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorJonas Schievink <[email protected]>2021-05-31 12:37:11 +0100
committerJonas Schievink <[email protected]>2021-06-03 17:09:21 +0100
commite5a2c6596ddd11b0d57042224ac7c1d7691ec33b (patch)
treef0476ad40103b5d3dea60f81fca32c63fe9618d7 /crates
parent7f9c4a59d9a84cd8c734286937439b5cd215be27 (diff)
Expand procedural attribute macros
Diffstat (limited to 'crates')
-rw-r--r--crates/hir/src/lib.rs16
-rw-r--r--crates/hir_def/src/lib.rs40
-rw-r--r--crates/hir_def/src/nameres/collector.rs56
-rw-r--r--crates/hir_expand/src/db.rs11
-rw-r--r--crates/hir_expand/src/input.rs19
-rw-r--r--crates/hir_expand/src/lib.rs24
-rw-r--r--crates/hir_expand/src/proc_macro.rs7
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
58use attr::Attr;
58use base_db::{impl_intern_key, salsa, CrateId}; 59use base_db::{impl_intern_key, salsa, CrateId};
59use hir_expand::{ 60use 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
773fn 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 &macro_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
24use crate::{ 24use 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 {
223enum MacroDirectiveKind { 223enum 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
229struct DefData<'a> { 229struct 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
14use crate::{ 14use 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, &macro_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, &macro_arg.0, attr_arg)
381} 386}
382 387
383fn is_self_replicating(from: &SyntaxNode, to: &SyntaxNode) -> bool { 388fn 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`.
58fn 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)]
50mod tests { 69mod 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
263impl MacroCallKind { 276impl 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 }