From a68ff269a921b4a3d19256a0f53596e84776105a Mon Sep 17 00:00:00 2001 From: Edwin Cheng Date: Tue, 15 Dec 2020 14:39:15 +0800 Subject: Expand statements for mbe in lowering --- crates/hir_def/src/body/lower.rs | 216 ++++++++++++++++++++++++--------------- crates/hir_def/src/item_tree.rs | 3 + crates/hir_expand/src/db.rs | 3 +- 3 files changed, 139 insertions(+), 83 deletions(-) (limited to 'crates') diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs index 689a3274c..6c0de3ee8 100644 --- a/crates/hir_def/src/body/lower.rs +++ b/crates/hir_def/src/body/lower.rs @@ -548,62 +548,83 @@ impl ExprCollector<'_> { } } ast::Expr::MacroCall(e) => { - if let Some(name) = e.is_macro_rules().map(|it| it.as_name()) { - let mac = MacroDefId { - krate: Some(self.expander.module.krate), - ast_id: Some(self.expander.ast_id(&e)), - kind: MacroDefKind::Declarative, - local_inner: false, - }; - self.body.item_scope.define_legacy_macro(name, mac); - - // FIXME: do we still need to allocate this as missing ? - self.alloc_expr(Expr::Missing, syntax_ptr) - } else { - // File containing the macro call. Expansion errors will be attached here. - let outer_file = self.expander.current_file_id; - - let macro_call = self.expander.to_source(AstPtr::new(&e)); - let res = self.expander.enter_expand(self.db, Some(&self.body.item_scope), e); - - match res.err { - Some(ExpandError::UnresolvedProcMacro) => { - self.source_map.diagnostics.push(BodyDiagnostic::UnresolvedProcMacro( - UnresolvedProcMacro { - file: outer_file, - node: syntax_ptr.clone().into(), - precise_location: None, - macro_name: None, - }, - )); - } - Some(err) => { - self.source_map.diagnostics.push(BodyDiagnostic::MacroError( - MacroError { - file: outer_file, - node: syntax_ptr.clone().into(), - message: err.to_string(), - }, - )); - } - None => {} - } + let mut ids = vec![]; + self.collect_macro_call(e, syntax_ptr.clone(), |this, expansion| { + ids.push(match expansion { + Some(it) => this.collect_expr(it), + None => this.alloc_expr(Expr::Missing, syntax_ptr.clone()), + }) + }); + ids[0] + } + } + } - match res.value { - Some((mark, expansion)) => { - self.source_map - .expansions - .insert(macro_call, self.expander.current_file_id); - - let item_tree = self.db.item_tree(self.expander.current_file_id); - self.item_trees.insert(self.expander.current_file_id, item_tree); - let id = self.collect_expr(expansion); - self.expander.exit(self.db, mark); - id - } - None => self.alloc_expr(Expr::Missing, syntax_ptr), + fn collect_macro_call), T: ast::AstNode>( + &mut self, + e: ast::MacroCall, + syntax_ptr: AstPtr, + mut collector: F, + ) { + if let Some(name) = e.is_macro_rules().map(|it| it.as_name()) { + let mac = MacroDefId { + krate: Some(self.expander.module.krate), + ast_id: Some(self.expander.ast_id(&e)), + kind: MacroDefKind::Declarative, + local_inner: false, + }; + self.body.item_scope.define_legacy_macro(name, mac); + + // FIXME: do we still need to allocate this as missing ? + collector(self, None); + } else { + // File containing the macro call. Expansion errors will be attached here. + let outer_file = self.expander.current_file_id; + + let macro_call = self.expander.to_source(AstPtr::new(&e)); + let res = self.expander.enter_expand(self.db, Some(&self.body.item_scope), e); + + match &res.err { + Some(ExpandError::UnresolvedProcMacro) => { + self.source_map.diagnostics.push(BodyDiagnostic::UnresolvedProcMacro( + UnresolvedProcMacro { + file: outer_file, + node: syntax_ptr.into(), + precise_location: None, + macro_name: None, + }, + )); + } + Some(err) => { + self.source_map.diagnostics.push(BodyDiagnostic::MacroError(MacroError { + file: outer_file, + node: syntax_ptr.into(), + message: err.to_string(), + })); + } + None => {} + } + + match res.value { + Some((mark, expansion)) => { + // FIXME: Statements are too complicated to recover from error for now. + // It is because we don't have any hygenine for local variable expansion right now. + if T::can_cast(syntax::SyntaxKind::MACRO_STMTS) && res.err.is_some() { + self.expander.exit(self.db, mark); + collector(self, None); + } else { + self.source_map + .expansions + .insert(macro_call, self.expander.current_file_id); + + let item_tree = self.db.item_tree(self.expander.current_file_id); + self.item_trees.insert(self.expander.current_file_id, item_tree); + + collector(self, Some(expansion)); + self.expander.exit(self.db, mark); } } + None => collector(self, None), } } } @@ -642,44 +663,75 @@ impl ExprCollector<'_> { } } - fn collect_block(&mut self, block: ast::BlockExpr) -> ExprId { - let syntax_node_ptr = AstPtr::new(&block.clone().into()); - self.collect_block_items(&block); - let statements = block - .statements() - .filter_map(|s| { - 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)); - Statement::Let { pat, type_ref, initializer } - } - ast::Stmt::ExprStmt(stmt) => { - self.check_cfg(&stmt)?; + fn collect_stmt(&mut self, s: ast::Stmt) -> Option> { + let stmt = + match s { + ast::Stmt::LetStmt(stmt) => { + self.check_cfg(&stmt)?; - Statement::Expr(self.collect_expr_opt(stmt.expr())) - } - ast::Stmt::Item(item) => { - self.check_cfg(&item)?; + 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; + this.collect_stmts_items(statements.statements()); - return None; + 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))); + } + } + None => { + stmts.push(Statement::Expr( + this.alloc_expr(Expr::Missing, syntax_ptr.clone()), + )); + } + } + }); + stmts + } else { + vec![Statement::Expr(self.collect_expr_opt(stmt.expr()))] } - }; - Some(stmt) - }) - .collect(); + } + ast::Stmt::Item(item) => { + self.check_cfg(&item)?; + + return None; + } + }; + + Some(stmt) + } + + fn collect_block(&mut self, block: ast::BlockExpr) -> ExprId { + let syntax_node_ptr = AstPtr::new(&block.clone().into()); + self.collect_stmts_items(block.statements()); + let statements = + block.statements().filter_map(|s| self.collect_stmt(s)).flatten().collect(); let tail = block.expr().map(|e| self.collect_expr(e)); self.alloc_expr(Expr::Block { statements, tail, label: None }, syntax_node_ptr) } - fn collect_block_items(&mut self, block: &ast::BlockExpr) { + fn collect_stmts_items(&mut self, stmts: ast::AstChildren) { let container = ContainerId::DefWithBodyId(self.def); - let items = block - .statements() + let items = stmts .filter_map(|stmt| match stmt { ast::Stmt::Item(it) => Some(it), ast::Stmt::LetStmt(_) | ast::Stmt::ExprStmt(_) => None, diff --git a/crates/hir_def/src/item_tree.rs b/crates/hir_def/src/item_tree.rs index b08167281..864fad170 100644 --- a/crates/hir_def/src/item_tree.rs +++ b/crates/hir_def/src/item_tree.rs @@ -94,6 +94,9 @@ impl ItemTree { ast::MacroItems(items) => { ctx.lower_module_items(&items) }, + ast::MacroStmts(stmts) => { + ctx.lower_inner_items(stmts.syntax()) + }, // Macros can expand to expressions. We return an empty item tree in this case, but // still need to collect inner items. ast::Expr(e) => { diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs index ffb70f723..7fc700e82 100644 --- a/crates/hir_expand/src/db.rs +++ b/crates/hir_expand/src/db.rs @@ -395,7 +395,8 @@ fn to_fragment_kind(db: &dyn AstDatabase, id: MacroCallId) -> FragmentKind { FragmentKind::Expr } // FIXME: Expand to statements in appropriate positions; HIR lowering needs to handle that - EXPR_STMT | BLOCK_EXPR => FragmentKind::Expr, + EXPR_STMT => FragmentKind::Statements, + BLOCK_EXPR => FragmentKind::Expr, ARG_LIST => FragmentKind::Expr, TRY_EXPR => FragmentKind::Expr, TUPLE_EXPR => FragmentKind::Expr, -- cgit v1.2.3 From 9da1eee436bdadfa3c80d309cd8bac385ef1d274 Mon Sep 17 00:00:00 2001 From: Edwin Cheng Date: Tue, 15 Dec 2020 14:39:26 +0800 Subject: Add regression test --- crates/hir_ty/src/tests/regression.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'crates') diff --git a/crates/hir_ty/src/tests/regression.rs b/crates/hir_ty/src/tests/regression.rs index da8170417..307a257b1 100644 --- a/crates/hir_ty/src/tests/regression.rs +++ b/crates/hir_ty/src/tests/regression.rs @@ -592,6 +592,30 @@ fn issue_4465_dollar_crate_at_type() { ); } +#[test] +fn issue_6811() { + check_infer( + r#" + macro_rules! profile_function { + () => { + let _a = 1; + let _b = 1; + }; + } + fn main() { + profile_function!(); + } + "#, + expect![[r#" + !3..5 '_a': i32 + !6..7 '1': i32 + !11..13 '_b': i32 + !14..15 '1': i32 + 103..131 '{ ...!(); }': () + "#]], + ); +} + #[test] fn issue_4053_diesel_where_clauses() { check_infer( -- cgit v1.2.3 From 1f4da7098c46a81784a099d80ba63539f94092d2 Mon Sep 17 00:00:00 2001 From: Edwin Cheng Date: Tue, 15 Dec 2020 17:25:59 +0800 Subject: Remove obsolete comment --- crates/hir_expand/src/db.rs | 1 - 1 file changed, 1 deletion(-) (limited to 'crates') diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs index 7fc700e82..11b5b98c8 100644 --- a/crates/hir_expand/src/db.rs +++ b/crates/hir_expand/src/db.rs @@ -394,7 +394,6 @@ fn to_fragment_kind(db: &dyn AstDatabase, id: MacroCallId) -> FragmentKind { // FIXME: Handle Pattern FragmentKind::Expr } - // FIXME: Expand to statements in appropriate positions; HIR lowering needs to handle that EXPR_STMT => FragmentKind::Statements, BLOCK_EXPR => FragmentKind::Expr, ARG_LIST => FragmentKind::Expr, -- cgit v1.2.3