about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJonas Schievink <jonasschievink@gmail.com>2021-04-10 23:12:02 +0200
committerJonas Schievink <jonasschievink@gmail.com>2021-04-11 01:25:50 +0200
commite2c1da36f59cd99d4da4c1d5f8f323626d3dbe61 (patch)
treee06290db93699c71d1e4c752de6563ff787bd5fa
parentbd675c8a8bdd3fda239bee3d3f31acd8679655b9 (diff)
downloadrust-e2c1da36f59cd99d4da4c1d5f8f323626d3dbe61.tar.gz
rust-e2c1da36f59cd99d4da4c1d5f8f323626d3dbe61.zip
Support macros in pattern position
-rw-r--r--crates/hir_def/src/body/lower.rs34
-rw-r--r--crates/hir_def/src/item_tree.rs5
-rw-r--r--crates/hir_def/src/item_tree/lower.rs2
-rw-r--r--crates/hir_expand/src/db.rs1
-rw-r--r--crates/hir_ty/src/tests/macros.rs2
-rw-r--r--crates/hir_ty/src/tests/patterns.rs28
-rw-r--r--crates/ide/src/goto_definition.rs26
7 files changed, 88 insertions, 10 deletions
diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs
index bfb75a8a599..ed07d692812 100644
--- a/crates/hir_def/src/body/lower.rs
+++ b/crates/hir_def/src/body/lower.rs
@@ -531,8 +531,9 @@ impl ExprCollector<'_> {
                 }
             }
             ast::Expr::MacroCall(e) => {
+                let macro_ptr = AstPtr::new(&e);
                 let mut ids = vec![];
-                self.collect_macro_call(e, syntax_ptr.clone(), true, |this, expansion| {
+                self.collect_macro_call(e, macro_ptr, true, |this, expansion| {
                     ids.push(match expansion {
                         Some(it) => this.collect_expr(it),
                         None => this.alloc_expr(Expr::Missing, syntax_ptr.clone()),
@@ -555,7 +556,7 @@ impl ExprCollector<'_> {
     fn collect_macro_call<F: FnMut(&mut Self, Option<T>), T: ast::AstNode>(
         &mut self,
         e: ast::MacroCall,
-        syntax_ptr: AstPtr<ast::Expr>,
+        syntax_ptr: AstPtr<ast::MacroCall>,
         is_error_recoverable: bool,
         mut collector: F,
     ) {
@@ -643,10 +644,14 @@ impl ExprCollector<'_> {
 
                 // Note that macro could be expended to multiple statements
                 if let Some(ast::Expr::MacroCall(m)) = stmt.expr() {
+                    let macro_ptr = AstPtr::new(&m);
                     let syntax_ptr = AstPtr::new(&stmt.expr().unwrap());
 
-                    self.collect_macro_call(m, syntax_ptr.clone(), false, |this, expansion| {
-                        match expansion {
+                    self.collect_macro_call(
+                        m,
+                        macro_ptr,
+                        false,
+                        |this, expansion| match expansion {
                             Some(expansion) => {
                                 let statements: ast::MacroStmts = expansion;
 
@@ -660,8 +665,8 @@ impl ExprCollector<'_> {
                                 let expr = this.alloc_expr(Expr::Missing, syntax_ptr.clone());
                                 this.statements_in_scope.push(Statement::Expr(expr));
                             }
-                        }
-                    });
+                        },
+                    );
                 } else {
                     let expr = self.collect_expr_opt(stmt.expr());
                     self.statements_in_scope.push(Statement::Expr(expr));
@@ -848,8 +853,23 @@ impl ExprCollector<'_> {
                     Pat::Missing
                 }
             }
+            ast::Pat::MacroPat(mac) => match mac.macro_call() {
+                Some(call) => {
+                    let macro_ptr = AstPtr::new(&call);
+                    let mut pat = None;
+                    self.collect_macro_call(call, macro_ptr, true, |this, expanded_pat| {
+                        pat = Some(this.collect_pat_opt(expanded_pat));
+                    });
+
+                    match pat {
+                        Some(pat) => return pat,
+                        None => Pat::Missing,
+                    }
+                }
+                None => Pat::Missing,
+            },
             // FIXME: implement
-            ast::Pat::RangePat(_) | ast::Pat::MacroPat(_) => Pat::Missing,
+            ast::Pat::RangePat(_) => Pat::Missing,
         };
         let ptr = AstPtr::new(&pat);
         self.alloc_pat(pattern, Either::Left(ptr))
diff --git a/crates/hir_def/src/item_tree.rs b/crates/hir_def/src/item_tree.rs
index 2406624862d..94e08f83590 100644
--- a/crates/hir_def/src/item_tree.rs
+++ b/crates/hir_def/src/item_tree.rs
@@ -99,6 +99,11 @@ impl ItemTree {
                     // items.
                     ctx.lower_macro_stmts(stmts)
                 },
+                ast::Pat(_pat) => {
+                    // FIXME: This occurs because macros in pattern position are treated as inner
+                    // items and expanded during block DefMap computation
+                    return Default::default();
+                },
                 ast::Expr(e) => {
                     // Macros can expand to expressions. We return an empty item tree in this case, but
                     // still need to collect inner items.
diff --git a/crates/hir_def/src/item_tree/lower.rs b/crates/hir_def/src/item_tree/lower.rs
index c5629af2401..45b099cf319 100644
--- a/crates/hir_def/src/item_tree/lower.rs
+++ b/crates/hir_def/src/item_tree/lower.rs
@@ -189,7 +189,7 @@ impl Ctx {
                                 block_stack.push(self.source_ast_id_map.ast_id(&block));
                             },
                             ast::Item(item) => {
-                                // FIXME: This triggers for macro calls in expression position
+                                // FIXME: This triggers for macro calls in expression/pattern/type position
                                 let mod_items = self.lower_mod_item(&item, true);
                                 let current_block = block_stack.last();
                                 if let (Some(mod_items), Some(block)) = (mod_items, current_block) {
diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs
index 10fe6082153..ca705ee9d4d 100644
--- a/crates/hir_expand/src/db.rs
+++ b/crates/hir_expand/src/db.rs
@@ -439,6 +439,7 @@ fn to_fragment_kind(db: &dyn AstDatabase, id: MacroCallId) -> FragmentKind {
     match parent.kind() {
         MACRO_ITEMS | SOURCE_FILE => FragmentKind::Items,
         MACRO_STMTS => FragmentKind::Statements,
+        MACRO_PAT => FragmentKind::Pattern,
         ITEM_LIST => FragmentKind::Items,
         LET_STMT => {
             // FIXME: Handle LHS Pattern
diff --git a/crates/hir_ty/src/tests/macros.rs b/crates/hir_ty/src/tests/macros.rs
index 86e3d8b86ef..b8e373ed889 100644
--- a/crates/hir_ty/src/tests/macros.rs
+++ b/crates/hir_ty/src/tests/macros.rs
@@ -1065,11 +1065,11 @@ fn macro_in_arm() {
         }
         "#,
         expect![[r#"
+            !0..2 '()': ()
             51..110 '{     ...  }; }': ()
             61..62 'x': u32
             65..107 'match ...     }': u32
             71..73 '()': ()
-            84..91 'unit!()': ()
             95..100 '92u32': u32
         "#]],
     );
diff --git a/crates/hir_ty/src/tests/patterns.rs b/crates/hir_ty/src/tests/patterns.rs
index 85a28e76b23..f514b3efe61 100644
--- a/crates/hir_ty/src/tests/patterns.rs
+++ b/crates/hir_ty/src/tests/patterns.rs
@@ -1,6 +1,6 @@
 use expect_test::expect;
 
-use super::{check_infer, check_infer_with_mismatches};
+use super::{check_infer, check_infer_with_mismatches, check_types};
 
 #[test]
 fn infer_pattern() {
@@ -825,3 +825,29 @@ fn foo(foo: Foo) {
         "#]],
     );
 }
+
+#[test]
+fn macro_pat() {
+    check_types(
+        r#"
+macro_rules! pat {
+    ($name:ident) => { Enum::Variant1($name) }
+}
+
+enum Enum {
+    Variant1(u8),
+    Variant2,
+}
+
+fn f(e: Enum) {
+    match e {
+        pat!(bind) => {
+            bind;
+          //^^^^ u8
+        }
+        Enum::Variant2 => {}
+    }
+}
+    "#,
+    )
+}
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs
index ca8ccb2da1f..d057d5402d3 100644
--- a/crates/ide/src/goto_definition.rs
+++ b/crates/ide/src/goto_definition.rs
@@ -1188,4 +1188,30 @@ pub fn gimme() -> theitem::TheItem {
 "#,
         );
     }
+
+    #[test]
+    fn goto_ident_from_pat_macro() {
+        check(
+            r#"
+macro_rules! pat {
+    ($name:ident) => { Enum::Variant1($name) }
+}
+
+enum Enum {
+    Variant1(u8),
+    Variant2,
+}
+
+fn f(e: Enum) {
+    match e {
+        pat!(bind) => {
+           //^^^^
+            bind$0
+        }
+        Enum::Variant2 => {}
+    }
+}
+"#,
+        );
+    }
 }