diff options
-rw-r--r-- | crates/hir_def/src/body/lower.rs | 216 | ||||
-rw-r--r-- | crates/hir_def/src/item_tree.rs | 3 | ||||
-rw-r--r-- | crates/hir_expand/src/db.rs | 4 | ||||
-rw-r--r-- | crates/hir_ty/src/tests/regression.rs | 24 |
4 files changed, 163 insertions, 84 deletions
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<'_> { | |||
548 | } | 548 | } |
549 | } | 549 | } |
550 | ast::Expr::MacroCall(e) => { | 550 | ast::Expr::MacroCall(e) => { |
551 | if let Some(name) = e.is_macro_rules().map(|it| it.as_name()) { | 551 | let mut ids = vec![]; |
552 | let mac = MacroDefId { | 552 | self.collect_macro_call(e, syntax_ptr.clone(), |this, expansion| { |
553 | krate: Some(self.expander.module.krate), | 553 | ids.push(match expansion { |
554 | ast_id: Some(self.expander.ast_id(&e)), | 554 | Some(it) => this.collect_expr(it), |
555 | kind: MacroDefKind::Declarative, | 555 | None => this.alloc_expr(Expr::Missing, syntax_ptr.clone()), |
556 | local_inner: false, | 556 | }) |
557 | }; | 557 | }); |
558 | self.body.item_scope.define_legacy_macro(name, mac); | 558 | ids[0] |
559 | 559 | } | |
560 | // FIXME: do we still need to allocate this as missing ? | 560 | } |
561 | self.alloc_expr(Expr::Missing, syntax_ptr) | 561 | } |
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 | } | ||
591 | 562 | ||
592 | match res.value { | 563 | fn collect_macro_call<F: FnMut(&mut Self, Option<T>), T: ast::AstNode>( |
593 | Some((mark, expansion)) => { | 564 | &mut self, |
594 | self.source_map | 565 | e: ast::MacroCall, |
595 | .expansions | 566 | syntax_ptr: AstPtr<ast::Expr>, |
596 | .insert(macro_call, self.expander.current_file_id); | 567 | mut collector: F, |
597 | 568 | ) { | |
598 | let item_tree = self.db.item_tree(self.expander.current_file_id); | 569 | if let Some(name) = e.is_macro_rules().map(|it| it.as_name()) { |
599 | self.item_trees.insert(self.expander.current_file_id, item_tree); | 570 | let mac = MacroDefId { |
600 | let id = self.collect_expr(expansion); | 571 | krate: Some(self.expander.module.krate), |
601 | self.expander.exit(self.db, mark); | 572 | ast_id: Some(self.expander.ast_id(&e)), |
602 | id | 573 | kind: MacroDefKind::Declarative, |
603 | } | 574 | local_inner: false, |
604 | None => self.alloc_expr(Expr::Missing, syntax_ptr), | 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); | ||
605 | } | 625 | } |
606 | } | 626 | } |
627 | None => collector(self, None), | ||
607 | } | 628 | } |
608 | } | 629 | } |
609 | } | 630 | } |
@@ -642,44 +663,75 @@ impl ExprCollector<'_> { | |||
642 | } | 663 | } |
643 | } | 664 | } |
644 | 665 | ||
645 | fn collect_block(&mut self, block: ast::BlockExpr) -> ExprId { | 666 | fn collect_stmt(&mut self, s: ast::Stmt) -> Option<Vec<Statement>> { |
646 | let syntax_node_ptr = AstPtr::new(&block.clone().into()); | 667 | let stmt = |
647 | self.collect_block_items(&block); | 668 | match s { |
648 | let statements = block | 669 | ast::Stmt::LetStmt(stmt) => { |
649 | .statements() | 670 | self.check_cfg(&stmt)?; |
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)?; | ||
662 | 671 | ||
663 | Statement::Expr(self.collect_expr_opt(stmt.expr())) | 672 | let pat = self.collect_pat_opt(stmt.pat()); |
664 | } | 673 | let type_ref = stmt.ty().map(|it| TypeRef::from_ast(&self.ctx(), it)); |
665 | ast::Stmt::Item(item) => { | 674 | let initializer = stmt.initializer().map(|e| self.collect_expr(e)); |
666 | self.check_cfg(&item)?; | 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()); | ||
667 | 690 | ||
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()))] | ||
669 | } | 710 | } |
670 | }; | 711 | } |
671 | Some(stmt) | 712 | ast::Stmt::Item(item) => { |
672 | }) | 713 | self.check_cfg(&item)?; |
673 | .collect(); | 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(); | ||
674 | let tail = block.expr().map(|e| self.collect_expr(e)); | 727 | let tail = block.expr().map(|e| self.collect_expr(e)); |
675 | self.alloc_expr(Expr::Block { statements, tail, label: None }, syntax_node_ptr) | 728 | self.alloc_expr(Expr::Block { statements, tail, label: None }, syntax_node_ptr) |
676 | } | 729 | } |
677 | 730 | ||
678 | fn collect_block_items(&mut self, block: &ast::BlockExpr) { | 731 | fn collect_stmts_items(&mut self, stmts: ast::AstChildren<ast::Stmt>) { |
679 | let container = ContainerId::DefWithBodyId(self.def); | 732 | let container = ContainerId::DefWithBodyId(self.def); |
680 | 733 | ||
681 | let items = block | 734 | let items = stmts |
682 | .statements() | ||
683 | .filter_map(|stmt| match stmt { | 735 | .filter_map(|stmt| match stmt { |
684 | ast::Stmt::Item(it) => Some(it), | 736 | ast::Stmt::Item(it) => Some(it), |
685 | ast::Stmt::LetStmt(_) | ast::Stmt::ExprStmt(_) => None, | 737 | 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 { | |||
94 | ast::MacroItems(items) => { | 94 | ast::MacroItems(items) => { |
95 | ctx.lower_module_items(&items) | 95 | ctx.lower_module_items(&items) |
96 | }, | 96 | }, |
97 | ast::MacroStmts(stmts) => { | ||
98 | ctx.lower_inner_items(stmts.syntax()) | ||
99 | }, | ||
97 | // Macros can expand to expressions. We return an empty item tree in this case, but | 100 | // Macros can expand to expressions. We return an empty item tree in this case, but |
98 | // still need to collect inner items. | 101 | // still need to collect inner items. |
99 | ast::Expr(e) => { | 102 | ast::Expr(e) => { |
diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs index ffb70f723..11b5b98c8 100644 --- a/crates/hir_expand/src/db.rs +++ b/crates/hir_expand/src/db.rs | |||
@@ -394,8 +394,8 @@ fn to_fragment_kind(db: &dyn AstDatabase, id: MacroCallId) -> FragmentKind { | |||
394 | // FIXME: Handle Pattern | 394 | // FIXME: Handle Pattern |
395 | FragmentKind::Expr | 395 | FragmentKind::Expr |
396 | } | 396 | } |
397 | // FIXME: Expand to statements in appropriate positions; HIR lowering needs to handle that | 397 | EXPR_STMT => FragmentKind::Statements, |
398 | EXPR_STMT | BLOCK_EXPR => FragmentKind::Expr, | 398 | BLOCK_EXPR => FragmentKind::Expr, |
399 | ARG_LIST => FragmentKind::Expr, | 399 | ARG_LIST => FragmentKind::Expr, |
400 | TRY_EXPR => FragmentKind::Expr, | 400 | TRY_EXPR => FragmentKind::Expr, |
401 | TUPLE_EXPR => FragmentKind::Expr, | 401 | TUPLE_EXPR => FragmentKind::Expr, |
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 | |||
@@ -593,6 +593,30 @@ fn issue_4465_dollar_crate_at_type() { | |||
593 | } | 593 | } |
594 | 594 | ||
595 | #[test] | 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 | |||
619 | #[test] | ||
596 | fn issue_4053_diesel_where_clauses() { | 620 | fn issue_4053_diesel_where_clauses() { |
597 | check_infer( | 621 | check_infer( |
598 | r#" | 622 | r#" |