Skip to content

Commit 39aae83

Browse files
Merge #6886
6886: Expand statements for macros in lowering r=matklad a=edwin0cheng Fixes #6811 Co-authored-by: Edwin Cheng <[email protected]>
2 parents 435d46b + 1f4da70 commit 39aae83

File tree

4 files changed

+163
-84
lines changed

4 files changed

+163
-84
lines changed

crates/hir_def/src/body/lower.rs

+134-82
Original file line numberDiff line numberDiff line change
@@ -548,62 +548,83 @@ impl ExprCollector<'_> {
548548
}
549549
}
550550
ast::Expr::MacroCall(e) => {
551-
if let Some(name) = e.is_macro_rules().map(|it| it.as_name()) {
552-
let mac = MacroDefId {
553-
krate: Some(self.expander.module.krate),
554-
ast_id: Some(self.expander.ast_id(&e)),
555-
kind: MacroDefKind::Declarative,
556-
local_inner: false,
557-
};
558-
self.body.item_scope.define_legacy_macro(name, mac);
559-
560-
// FIXME: do we still need to allocate this as missing ?
561-
self.alloc_expr(Expr::Missing, syntax_ptr)
562-
} else {
563-
// File containing the macro call. Expansion errors will be attached here.
564-
let outer_file = self.expander.current_file_id;
565-
566-
let macro_call = self.expander.to_source(AstPtr::new(&e));
567-
let res = self.expander.enter_expand(self.db, Some(&self.body.item_scope), e);
568-
569-
match res.err {
570-
Some(ExpandError::UnresolvedProcMacro) => {
571-
self.source_map.diagnostics.push(BodyDiagnostic::UnresolvedProcMacro(
572-
UnresolvedProcMacro {
573-
file: outer_file,
574-
node: syntax_ptr.clone().into(),
575-
precise_location: None,
576-
macro_name: None,
577-
},
578-
));
579-
}
580-
Some(err) => {
581-
self.source_map.diagnostics.push(BodyDiagnostic::MacroError(
582-
MacroError {
583-
file: outer_file,
584-
node: syntax_ptr.clone().into(),
585-
message: err.to_string(),
586-
},
587-
));
588-
}
589-
None => {}
590-
}
551+
let mut ids = vec![];
552+
self.collect_macro_call(e, syntax_ptr.clone(), |this, expansion| {
553+
ids.push(match expansion {
554+
Some(it) => this.collect_expr(it),
555+
None => this.alloc_expr(Expr::Missing, syntax_ptr.clone()),
556+
})
557+
});
558+
ids[0]
559+
}
560+
}
561+
}
591562

592-
match res.value {
593-
Some((mark, expansion)) => {
594-
self.source_map
595-
.expansions
596-
.insert(macro_call, self.expander.current_file_id);
597-
598-
let item_tree = self.db.item_tree(self.expander.current_file_id);
599-
self.item_trees.insert(self.expander.current_file_id, item_tree);
600-
let id = self.collect_expr(expansion);
601-
self.expander.exit(self.db, mark);
602-
id
603-
}
604-
None => self.alloc_expr(Expr::Missing, syntax_ptr),
563+
fn collect_macro_call<F: FnMut(&mut Self, Option<T>), T: ast::AstNode>(
564+
&mut self,
565+
e: ast::MacroCall,
566+
syntax_ptr: AstPtr<ast::Expr>,
567+
mut collector: F,
568+
) {
569+
if let Some(name) = e.is_macro_rules().map(|it| it.as_name()) {
570+
let mac = MacroDefId {
571+
krate: Some(self.expander.module.krate),
572+
ast_id: Some(self.expander.ast_id(&e)),
573+
kind: MacroDefKind::Declarative,
574+
local_inner: false,
575+
};
576+
self.body.item_scope.define_legacy_macro(name, mac);
577+
578+
// FIXME: do we still need to allocate this as missing ?
579+
collector(self, None);
580+
} else {
581+
// File containing the macro call. Expansion errors will be attached here.
582+
let outer_file = self.expander.current_file_id;
583+
584+
let macro_call = self.expander.to_source(AstPtr::new(&e));
585+
let res = self.expander.enter_expand(self.db, Some(&self.body.item_scope), e);
586+
587+
match &res.err {
588+
Some(ExpandError::UnresolvedProcMacro) => {
589+
self.source_map.diagnostics.push(BodyDiagnostic::UnresolvedProcMacro(
590+
UnresolvedProcMacro {
591+
file: outer_file,
592+
node: syntax_ptr.into(),
593+
precise_location: None,
594+
macro_name: None,
595+
},
596+
));
597+
}
598+
Some(err) => {
599+
self.source_map.diagnostics.push(BodyDiagnostic::MacroError(MacroError {
600+
file: outer_file,
601+
node: syntax_ptr.into(),
602+
message: err.to_string(),
603+
}));
604+
}
605+
None => {}
606+
}
607+
608+
match res.value {
609+
Some((mark, expansion)) => {
610+
// FIXME: Statements are too complicated to recover from error for now.
611+
// It is because we don't have any hygenine for local variable expansion right now.
612+
if T::can_cast(syntax::SyntaxKind::MACRO_STMTS) && res.err.is_some() {
613+
self.expander.exit(self.db, mark);
614+
collector(self, None);
615+
} else {
616+
self.source_map
617+
.expansions
618+
.insert(macro_call, self.expander.current_file_id);
619+
620+
let item_tree = self.db.item_tree(self.expander.current_file_id);
621+
self.item_trees.insert(self.expander.current_file_id, item_tree);
622+
623+
collector(self, Some(expansion));
624+
self.expander.exit(self.db, mark);
605625
}
606626
}
627+
None => collector(self, None),
607628
}
608629
}
609630
}
@@ -642,44 +663,75 @@ impl ExprCollector<'_> {
642663
}
643664
}
644665

645-
fn collect_block(&mut self, block: ast::BlockExpr) -> ExprId {
646-
let syntax_node_ptr = AstPtr::new(&block.clone().into());
647-
self.collect_block_items(&block);
648-
let statements = block
649-
.statements()
650-
.filter_map(|s| {
651-
let stmt = match s {
652-
ast::Stmt::LetStmt(stmt) => {
653-
self.check_cfg(&stmt)?;
654-
655-
let pat = self.collect_pat_opt(stmt.pat());
656-
let type_ref = stmt.ty().map(|it| TypeRef::from_ast(&self.ctx(), it));
657-
let initializer = stmt.initializer().map(|e| self.collect_expr(e));
658-
Statement::Let { pat, type_ref, initializer }
659-
}
660-
ast::Stmt::ExprStmt(stmt) => {
661-
self.check_cfg(&stmt)?;
666+
fn collect_stmt(&mut self, s: ast::Stmt) -> Option<Vec<Statement>> {
667+
let stmt =
668+
match s {
669+
ast::Stmt::LetStmt(stmt) => {
670+
self.check_cfg(&stmt)?;
662671

663-
Statement::Expr(self.collect_expr_opt(stmt.expr()))
664-
}
665-
ast::Stmt::Item(item) => {
666-
self.check_cfg(&item)?;
672+
let pat = self.collect_pat_opt(stmt.pat());
673+
let type_ref = stmt.ty().map(|it| TypeRef::from_ast(&self.ctx(), it));
674+
let initializer = stmt.initializer().map(|e| self.collect_expr(e));
675+
vec![Statement::Let { pat, type_ref, initializer }]
676+
}
677+
ast::Stmt::ExprStmt(stmt) => {
678+
self.check_cfg(&stmt)?;
679+
680+
// Note that macro could be expended to multiple statements
681+
if let Some(ast::Expr::MacroCall(m)) = stmt.expr() {
682+
let syntax_ptr = AstPtr::new(&stmt.expr().unwrap());
683+
let mut stmts = vec![];
684+
685+
self.collect_macro_call(m, syntax_ptr.clone(), |this, expansion| {
686+
match expansion {
687+
Some(expansion) => {
688+
let statements: ast::MacroStmts = expansion;
689+
this.collect_stmts_items(statements.statements());
667690

668-
return None;
691+
statements.statements().for_each(|stmt| {
692+
if let Some(mut r) = this.collect_stmt(stmt) {
693+
stmts.append(&mut r);
694+
}
695+
});
696+
if let Some(expr) = statements.expr() {
697+
stmts.push(Statement::Expr(this.collect_expr(expr)));
698+
}
699+
}
700+
None => {
701+
stmts.push(Statement::Expr(
702+
this.alloc_expr(Expr::Missing, syntax_ptr.clone()),
703+
));
704+
}
705+
}
706+
});
707+
stmts
708+
} else {
709+
vec![Statement::Expr(self.collect_expr_opt(stmt.expr()))]
669710
}
670-
};
671-
Some(stmt)
672-
})
673-
.collect();
711+
}
712+
ast::Stmt::Item(item) => {
713+
self.check_cfg(&item)?;
714+
715+
return None;
716+
}
717+
};
718+
719+
Some(stmt)
720+
}
721+
722+
fn collect_block(&mut self, block: ast::BlockExpr) -> ExprId {
723+
let syntax_node_ptr = AstPtr::new(&block.clone().into());
724+
self.collect_stmts_items(block.statements());
725+
let statements =
726+
block.statements().filter_map(|s| self.collect_stmt(s)).flatten().collect();
674727
let tail = block.expr().map(|e| self.collect_expr(e));
675728
self.alloc_expr(Expr::Block { statements, tail, label: None }, syntax_node_ptr)
676729
}
677730

678-
fn collect_block_items(&mut self, block: &ast::BlockExpr) {
731+
fn collect_stmts_items(&mut self, stmts: ast::AstChildren<ast::Stmt>) {
679732
let container = ContainerId::DefWithBodyId(self.def);
680733

681-
let items = block
682-
.statements()
734+
let items = stmts
683735
.filter_map(|stmt| match stmt {
684736
ast::Stmt::Item(it) => Some(it),
685737
ast::Stmt::LetStmt(_) | ast::Stmt::ExprStmt(_) => None,

crates/hir_def/src/item_tree.rs

+3
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,9 @@ impl ItemTree {
9494
ast::MacroItems(items) => {
9595
ctx.lower_module_items(&items)
9696
},
97+
ast::MacroStmts(stmts) => {
98+
ctx.lower_inner_items(stmts.syntax())
99+
},
97100
// Macros can expand to expressions. We return an empty item tree in this case, but
98101
// still need to collect inner items.
99102
ast::Expr(e) => {

crates/hir_expand/src/db.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -394,8 +394,8 @@ fn to_fragment_kind(db: &dyn AstDatabase, id: MacroCallId) -> FragmentKind {
394394
// FIXME: Handle Pattern
395395
FragmentKind::Expr
396396
}
397-
// FIXME: Expand to statements in appropriate positions; HIR lowering needs to handle that
398-
EXPR_STMT | BLOCK_EXPR => FragmentKind::Expr,
397+
EXPR_STMT => FragmentKind::Statements,
398+
BLOCK_EXPR => FragmentKind::Expr,
399399
ARG_LIST => FragmentKind::Expr,
400400
TRY_EXPR => FragmentKind::Expr,
401401
TUPLE_EXPR => FragmentKind::Expr,

crates/hir_ty/src/tests/regression.rs

+24
Original file line numberDiff line numberDiff line change
@@ -592,6 +592,30 @@ fn issue_4465_dollar_crate_at_type() {
592592
);
593593
}
594594

595+
#[test]
596+
fn issue_6811() {
597+
check_infer(
598+
r#"
599+
macro_rules! profile_function {
600+
() => {
601+
let _a = 1;
602+
let _b = 1;
603+
};
604+
}
605+
fn main() {
606+
profile_function!();
607+
}
608+
"#,
609+
expect![[r#"
610+
!3..5 '_a': i32
611+
!6..7 '1': i32
612+
!11..13 '_b': i32
613+
!14..15 '1': i32
614+
103..131 '{ ...!(); }': ()
615+
"#]],
616+
);
617+
}
618+
595619
#[test]
596620
fn issue_4053_diesel_where_clauses() {
597621
check_infer(

0 commit comments

Comments
 (0)