From e2c1da36f59cd99d4da4c1d5f8f323626d3dbe61 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Sat, 10 Apr 2021 23:12:02 +0200 Subject: Support macros in pattern position --- crates/hir_def/src/body/lower.rs | 34 +++++++++++++++++++++++++++------- crates/hir_def/src/item_tree.rs | 5 +++++ crates/hir_def/src/item_tree/lower.rs | 2 +- crates/hir_expand/src/db.rs | 1 + crates/hir_ty/src/tests/macros.rs | 2 +- crates/hir_ty/src/tests/patterns.rs | 28 +++++++++++++++++++++++++++- crates/ide/src/goto_definition.rs | 26 ++++++++++++++++++++++++++ 7 files changed, 88 insertions(+), 10 deletions(-) (limited to 'crates') diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs index bfb75a8a5..ed07d6928 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), T: ast::AstNode>( &mut self, e: ast::MacroCall, - syntax_ptr: AstPtr, + syntax_ptr: AstPtr, 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 240662486..94e08f835 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 c5629af24..45b099cf3 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 10fe60821..ca705ee9d 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 86e3d8b86..b8e373ed8 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 85a28e76b..f514b3efe 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 ca8ccb2da..d057d5402 100644 --- a/crates/ide/src/goto_definition.rs +++ b/crates/ide/src/goto_definition.rs @@ -1185,6 +1185,32 @@ pub mod theitem { pub fn gimme() -> theitem::TheItem { 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 => {} + } +} "#, ); } -- cgit v1.2.3