Support macros in pattern position

This commit is contained in:
Jonas Schievink 2021-04-10 23:12:02 +02:00
parent bd675c8a8b
commit e2c1da36f5
7 changed files with 88 additions and 10 deletions

View File

@ -531,8 +531,9 @@ impl ExprCollector<'_> {
} }
} }
ast::Expr::MacroCall(e) => { ast::Expr::MacroCall(e) => {
let macro_ptr = AstPtr::new(&e);
let mut ids = vec![]; 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 { ids.push(match expansion {
Some(it) => this.collect_expr(it), Some(it) => this.collect_expr(it),
None => this.alloc_expr(Expr::Missing, syntax_ptr.clone()), 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>( fn collect_macro_call<F: FnMut(&mut Self, Option<T>), T: ast::AstNode>(
&mut self, &mut self,
e: ast::MacroCall, e: ast::MacroCall,
syntax_ptr: AstPtr<ast::Expr>, syntax_ptr: AstPtr<ast::MacroCall>,
is_error_recoverable: bool, is_error_recoverable: bool,
mut collector: F, mut collector: F,
) { ) {
@ -643,10 +644,14 @@ impl ExprCollector<'_> {
// Note that macro could be expended to multiple statements // Note that macro could be expended to multiple statements
if let Some(ast::Expr::MacroCall(m)) = stmt.expr() { if let Some(ast::Expr::MacroCall(m)) = stmt.expr() {
let macro_ptr = AstPtr::new(&m);
let syntax_ptr = AstPtr::new(&stmt.expr().unwrap()); let syntax_ptr = AstPtr::new(&stmt.expr().unwrap());
self.collect_macro_call(m, syntax_ptr.clone(), false, |this, expansion| { self.collect_macro_call(
match expansion { m,
macro_ptr,
false,
|this, expansion| match expansion {
Some(expansion) => { Some(expansion) => {
let statements: ast::MacroStmts = expansion; let statements: ast::MacroStmts = expansion;
@ -660,8 +665,8 @@ impl ExprCollector<'_> {
let expr = this.alloc_expr(Expr::Missing, syntax_ptr.clone()); let expr = this.alloc_expr(Expr::Missing, syntax_ptr.clone());
this.statements_in_scope.push(Statement::Expr(expr)); this.statements_in_scope.push(Statement::Expr(expr));
} }
} },
}); );
} else { } else {
let expr = self.collect_expr_opt(stmt.expr()); let expr = self.collect_expr_opt(stmt.expr());
self.statements_in_scope.push(Statement::Expr(expr)); self.statements_in_scope.push(Statement::Expr(expr));
@ -848,8 +853,23 @@ impl ExprCollector<'_> {
Pat::Missing 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 // FIXME: implement
ast::Pat::RangePat(_) | ast::Pat::MacroPat(_) => Pat::Missing, ast::Pat::RangePat(_) => Pat::Missing,
}; };
let ptr = AstPtr::new(&pat); let ptr = AstPtr::new(&pat);
self.alloc_pat(pattern, Either::Left(ptr)) self.alloc_pat(pattern, Either::Left(ptr))

View File

@ -99,6 +99,11 @@ impl ItemTree {
// items. // items.
ctx.lower_macro_stmts(stmts) 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) => { ast::Expr(e) => {
// Macros can expand to expressions. We return an empty item tree in this case, but // Macros can expand to expressions. We return an empty item tree in this case, but
// still need to collect inner items. // still need to collect inner items.

View File

@ -189,7 +189,7 @@ impl Ctx {
block_stack.push(self.source_ast_id_map.ast_id(&block)); block_stack.push(self.source_ast_id_map.ast_id(&block));
}, },
ast::Item(item) => { 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 mod_items = self.lower_mod_item(&item, true);
let current_block = block_stack.last(); let current_block = block_stack.last();
if let (Some(mod_items), Some(block)) = (mod_items, current_block) { if let (Some(mod_items), Some(block)) = (mod_items, current_block) {

View File

@ -439,6 +439,7 @@ fn to_fragment_kind(db: &dyn AstDatabase, id: MacroCallId) -> FragmentKind {
match parent.kind() { match parent.kind() {
MACRO_ITEMS | SOURCE_FILE => FragmentKind::Items, MACRO_ITEMS | SOURCE_FILE => FragmentKind::Items,
MACRO_STMTS => FragmentKind::Statements, MACRO_STMTS => FragmentKind::Statements,
MACRO_PAT => FragmentKind::Pattern,
ITEM_LIST => FragmentKind::Items, ITEM_LIST => FragmentKind::Items,
LET_STMT => { LET_STMT => {
// FIXME: Handle LHS Pattern // FIXME: Handle LHS Pattern

View File

@ -1065,11 +1065,11 @@ fn macro_in_arm() {
} }
"#, "#,
expect![[r#" expect![[r#"
!0..2 '()': ()
51..110 '{ ... }; }': () 51..110 '{ ... }; }': ()
61..62 'x': u32 61..62 'x': u32
65..107 'match ... }': u32 65..107 'match ... }': u32
71..73 '()': () 71..73 '()': ()
84..91 'unit!()': ()
95..100 '92u32': u32 95..100 '92u32': u32
"#]], "#]],
); );

View File

@ -1,6 +1,6 @@
use expect_test::expect; use expect_test::expect;
use super::{check_infer, check_infer_with_mismatches}; use super::{check_infer, check_infer_with_mismatches, check_types};
#[test] #[test]
fn infer_pattern() { 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 => {}
}
}
"#,
)
}

View File

@ -1185,6 +1185,32 @@ pub mod theitem {
pub fn gimme() -> theitem::TheItem { pub fn gimme() -> theitem::TheItem {
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 => {}
}
}
"#, "#,
); );
} }