aboutsummaryrefslogtreecommitdiff
path: root/crates/hir_expand
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/hir_expand
parent7f9c4a59d9a84cd8c734286937439b5cd215be27 (diff)
Expand procedural attribute macros
Diffstat (limited to 'crates/hir_expand')
-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
4 files changed, 54 insertions, 7 deletions
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 }