8463: Support macros in pattern position r=jonas-schievink a=jonas-schievink

This was fairly easy, because patterns are limited to bodies, so almost all changes were inside body lowering.

Co-authored-by: Jonas Schievink <jonasschievink@gmail.com>
This commit is contained in:
bors[bot] 2021-04-10 23:33:18 +00:00 committed by GitHub
commit eccd0efedb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 88 additions and 10 deletions

View file

@ -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))

View file

@ -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.

View file

@ -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) {

View file

@ -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

View file

@ -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
"#]],
);

View file

@ -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 => {}
}
}
"#,
)
}

View file

@ -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 => {}
}
}
"#,
);
}