Skip to content

Fix macro expansion for statements without semicolon #8044

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

105 changes: 58 additions & 47 deletions crates/hir_def/src/body/lower.rs
Original file line number Diff line number Diff line change
Expand Up @@ -519,21 +519,33 @@ impl ExprCollector<'_> {
}
ast::Expr::MacroCall(e) => {
let mut ids = vec![];
self.collect_macro_call(e, syntax_ptr.clone(), |this, expansion| {
self.collect_macro_call(e, syntax_ptr.clone(), true, |this, expansion| {
ids.push(match expansion {
Some(it) => this.collect_expr(it),
None => this.alloc_expr(Expr::Missing, syntax_ptr.clone()),
})
});
ids[0]
}
ast::Expr::MacroStmts(e) => {
// FIXME: these statements should be held by some hir containter
for stmt in e.statements() {
self.collect_stmt(stmt);
}
if let Some(expr) = e.expr() {
self.collect_expr(expr)
} else {
self.alloc_expr(Expr::Missing, syntax_ptr)
}
}
}
}

fn collect_macro_call<F: FnMut(&mut Self, Option<T>), T: ast::AstNode>(
&mut self,
e: ast::MacroCall,
syntax_ptr: AstPtr<ast::Expr>,
is_error_recoverable: bool,
mut collector: F,
) {
// File containing the macro call. Expansion errors will be attached here.
Expand Down Expand Up @@ -567,7 +579,7 @@ impl ExprCollector<'_> {
Some((mark, expansion)) => {
// FIXME: Statements are too complicated to recover from error for now.
// It is because we don't have any hygiene for local variable expansion right now.
if T::can_cast(syntax::SyntaxKind::MACRO_STMTS) && res.err.is_some() {
if !is_error_recoverable && res.err.is_some() {
self.expander.exit(self.db, mark);
collector(self, None);
} else {
Expand All @@ -591,56 +603,55 @@ impl ExprCollector<'_> {
}

fn collect_stmt(&mut self, s: ast::Stmt) -> Option<Vec<Statement>> {
let stmt =
match s {
ast::Stmt::LetStmt(stmt) => {
self.check_cfg(&stmt)?;

let pat = self.collect_pat_opt(stmt.pat());
let type_ref = stmt.ty().map(|it| TypeRef::from_ast(&self.ctx(), it));
let initializer = stmt.initializer().map(|e| self.collect_expr(e));
vec![Statement::Let { pat, type_ref, initializer }]
}
ast::Stmt::ExprStmt(stmt) => {
self.check_cfg(&stmt)?;

// Note that macro could be expended to multiple statements
if let Some(ast::Expr::MacroCall(m)) = stmt.expr() {
let syntax_ptr = AstPtr::new(&stmt.expr().unwrap());
let mut stmts = vec![];

self.collect_macro_call(m, syntax_ptr.clone(), |this, expansion| {
match expansion {
Some(expansion) => {
let statements: ast::MacroStmts = expansion;

statements.statements().for_each(|stmt| {
if let Some(mut r) = this.collect_stmt(stmt) {
stmts.append(&mut r);
}
});
if let Some(expr) = statements.expr() {
stmts.push(Statement::Expr(this.collect_expr(expr)));
let stmt = match s {
ast::Stmt::LetStmt(stmt) => {
self.check_cfg(&stmt)?;

let pat = self.collect_pat_opt(stmt.pat());
let type_ref = stmt.ty().map(|it| TypeRef::from_ast(&self.ctx(), it));
let initializer = stmt.initializer().map(|e| self.collect_expr(e));
vec![Statement::Let { pat, type_ref, initializer }]
}
ast::Stmt::ExprStmt(stmt) => {
self.check_cfg(&stmt)?;

// Note that macro could be expended to multiple statements
if let Some(ast::Expr::MacroCall(m)) = stmt.expr() {
let syntax_ptr = AstPtr::new(&stmt.expr().unwrap());
let mut stmts = vec![];

self.collect_macro_call(m, syntax_ptr.clone(), false, |this, expansion| {
match expansion {
Some(expansion) => {
let statements: ast::MacroStmts = expansion;

statements.statements().for_each(|stmt| {
if let Some(mut r) = this.collect_stmt(stmt) {
stmts.append(&mut r);
}
}
None => {
stmts.push(Statement::Expr(
this.alloc_expr(Expr::Missing, syntax_ptr.clone()),
));
});
if let Some(expr) = statements.expr() {
stmts.push(Statement::Expr(this.collect_expr(expr)));
}
}
});
stmts
} else {
vec![Statement::Expr(self.collect_expr_opt(stmt.expr()))]
}
None => {
stmts.push(Statement::Expr(
this.alloc_expr(Expr::Missing, syntax_ptr.clone()),
));
}
}
});
stmts
} else {
vec![Statement::Expr(self.collect_expr_opt(stmt.expr()))]
}
ast::Stmt::Item(item) => {
self.check_cfg(&item)?;
}
ast::Stmt::Item(item) => {
self.check_cfg(&item)?;

return None;
}
};
return None;
}
};

Some(stmt)
}
Expand Down
5 changes: 5 additions & 0 deletions crates/hir_def/src/item_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,11 @@ impl ItemTree {
// still need to collect inner items.
ctx.lower_inner_items(e.syntax())
},
ast::ExprStmt(stmt) => {
// Macros can expand to stmt. We return an empty item tree in this case, but
// still need to collect inner items.
ctx.lower_inner_items(stmt.syntax())
},
_ => {
panic!("cannot create item tree from {:?} {}", syntax, syntax);
},
Expand Down
3 changes: 2 additions & 1 deletion crates/hir_expand/src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -401,13 +401,14 @@ fn to_fragment_kind(db: &dyn AstDatabase, id: MacroCallId) -> FragmentKind {

match parent.kind() {
MACRO_ITEMS | SOURCE_FILE => FragmentKind::Items,
MACRO_STMTS => FragmentKind::Statement,
ITEM_LIST => FragmentKind::Items,
LET_STMT => {
// FIXME: Handle Pattern
FragmentKind::Expr
}
EXPR_STMT => FragmentKind::Statements,
BLOCK_EXPR => FragmentKind::Expr,
BLOCK_EXPR => FragmentKind::Statements,
ARG_LIST => FragmentKind::Expr,
TRY_EXPR => FragmentKind::Expr,
TUPLE_EXPR => FragmentKind::Expr,
Expand Down
16 changes: 16 additions & 0 deletions crates/hir_ty/src/tests/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,22 @@ fn expr_macro_expanded_in_various_places() {
);
}

#[test]
fn expr_macro_expanded_in_stmts() {
check_infer(
r#"
macro_rules! id { ($($es:tt)*) => { $($es)* } }
fn foo() {
id! { let a = (); }
}
"#,
expect![[r#"
!0..8 'leta=();': ()
57..84 '{ ...); } }': ()
"#]],
);
}

#[test]
fn infer_type_value_macro_having_same_name() {
check_infer(
Expand Down
11 changes: 5 additions & 6 deletions crates/mbe/src/tests.rs
6 changes: 3 additions & 3 deletions crates/parser/src/grammar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,11 @@ pub(crate) mod fragments {
}

pub(crate) fn stmt(p: &mut Parser) {
expressions::stmt(p, expressions::StmtWithSemi::No)
expressions::stmt(p, expressions::StmtWithSemi::No, true)
}

pub(crate) fn stmt_optional_semi(p: &mut Parser) {
expressions::stmt(p, expressions::StmtWithSemi::Optional)
expressions::stmt(p, expressions::StmtWithSemi::Optional, false)
}

pub(crate) fn opt_visibility(p: &mut Parser) {
Expand Down Expand Up @@ -133,7 +133,7 @@ pub(crate) mod fragments {
continue;
}

expressions::stmt(p, expressions::StmtWithSemi::Optional);
expressions::stmt(p, expressions::StmtWithSemi::Optional, true);
}

m.complete(p, MACRO_STMTS);
Expand Down
6 changes: 3 additions & 3 deletions crates/parser/src/grammar/expressions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ fn is_expr_stmt_attr_allowed(kind: SyntaxKind) -> bool {
!forbid
}

pub(super) fn stmt(p: &mut Parser, with_semi: StmtWithSemi) {
pub(super) fn stmt(p: &mut Parser, with_semi: StmtWithSemi, prefer_expr: bool) {
let m = p.start();
// test attr_on_expr_stmt
// fn foo() {
Expand Down Expand Up @@ -90,7 +90,7 @@ pub(super) fn stmt(p: &mut Parser, with_semi: StmtWithSemi) {
p.error(format!("attributes are not allowed on {:?}", kind));
}

if p.at(T!['}']) {
if p.at(T!['}']) || (prefer_expr && p.at(EOF)) {
// test attr_on_last_expr_in_block
// fn foo() {
// { #[A] bar!()? }
Expand Down Expand Up @@ -198,7 +198,7 @@ pub(super) fn expr_block_contents(p: &mut Parser) {
continue;
}

stmt(p, StmtWithSemi::Yes)
stmt(p, StmtWithSemi::Yes, false)
}
}

Expand Down
10 changes: 8 additions & 2 deletions crates/syntax/src/ast/generated/nodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1336,6 +1336,7 @@ pub enum Expr {
Literal(Literal),
LoopExpr(LoopExpr),
MacroCall(MacroCall),
MacroStmts(MacroStmts),
MatchExpr(MatchExpr),
MethodCallExpr(MethodCallExpr),
ParenExpr(ParenExpr),
Expand Down Expand Up @@ -3034,6 +3035,9 @@ impl From<LoopExpr> for Expr {
impl From<MacroCall> for Expr {
fn from(node: MacroCall) -> Expr { Expr::MacroCall(node) }
}
impl From<MacroStmts> for Expr {
fn from(node: MacroStmts) -> Expr { Expr::MacroStmts(node) }
}
impl From<MatchExpr> for Expr {
fn from(node: MatchExpr) -> Expr { Expr::MatchExpr(node) }
}
Expand Down Expand Up @@ -3078,8 +3082,8 @@ impl AstNode for Expr {
match kind {
ARRAY_EXPR | AWAIT_EXPR | BIN_EXPR | BLOCK_EXPR | BOX_EXPR | BREAK_EXPR | CALL_EXPR
| CAST_EXPR | CLOSURE_EXPR | CONTINUE_EXPR | EFFECT_EXPR | FIELD_EXPR | FOR_EXPR
| IF_EXPR | INDEX_EXPR | LITERAL | LOOP_EXPR | MACRO_CALL | MATCH_EXPR
| METHOD_CALL_EXPR | PAREN_EXPR | PATH_EXPR | PREFIX_EXPR | RANGE_EXPR
| IF_EXPR | INDEX_EXPR | LITERAL | LOOP_EXPR | MACRO_CALL | MACRO_STMTS
| MATCH_EXPR | METHOD_CALL_EXPR | PAREN_EXPR | PATH_EXPR | PREFIX_EXPR | RANGE_EXPR
| RECORD_EXPR | REF_EXPR | RETURN_EXPR | TRY_EXPR | TUPLE_EXPR | WHILE_EXPR
| YIELD_EXPR => true,
_ => false,
Expand All @@ -3105,6 +3109,7 @@ impl AstNode for Expr {
LITERAL => Expr::Literal(Literal { syntax }),
LOOP_EXPR => Expr::LoopExpr(LoopExpr { syntax }),
MACRO_CALL => Expr::MacroCall(MacroCall { syntax }),
MACRO_STMTS => Expr::MacroStmts(MacroStmts { syntax }),
MATCH_EXPR => Expr::MatchExpr(MatchExpr { syntax }),
METHOD_CALL_EXPR => Expr::MethodCallExpr(MethodCallExpr { syntax }),
PAREN_EXPR => Expr::ParenExpr(ParenExpr { syntax }),
Expand Down Expand Up @@ -3142,6 +3147,7 @@ impl AstNode for Expr {
Expr::Literal(it) => &it.syntax,
Expr::LoopExpr(it) => &it.syntax,
Expr::MacroCall(it) => &it.syntax,
Expr::MacroStmts(it) => &it.syntax,
Expr::MatchExpr(it) => &it.syntax,
Expr::MethodCallExpr(it) => &it.syntax,
Expr::ParenExpr(it) => &it.syntax,
Expand Down
2 changes: 1 addition & 1 deletion xtask/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ anyhow = "1.0.26"
flate2 = "1.0"
proc-macro2 = "1.0.8"
quote = "1.0.2"
ungrammar = "=1.11"
ungrammar = "=1.12"
walkdir = "2.3.1"
write-json = "0.1.0"
xshell = "0.1"
Expand Down