aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/hir_def/src/body/lower.rs34
-rw-r--r--crates/hir_def/src/item_tree.rs5
-rw-r--r--crates/hir_def/src/item_tree/lower.rs2
-rw-r--r--crates/hir_expand/src/db.rs1
-rw-r--r--crates/hir_ty/src/tests/macros.rs2
-rw-r--r--crates/hir_ty/src/tests/patterns.rs28
-rw-r--r--crates/ide/src/goto_definition.rs26
-rw-r--r--crates/ide_assists/src/handlers/extract_function.rs128
8 files changed, 197 insertions, 29 deletions
diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs
index bfb75a8a5..ed07d6928 100644
--- a/crates/hir_def/src/body/lower.rs
+++ b/crates/hir_def/src/body/lower.rs
@@ -531,8 +531,9 @@ impl ExprCollector<'_> {
531 } 531 }
532 } 532 }
533 ast::Expr::MacroCall(e) => { 533 ast::Expr::MacroCall(e) => {
534 let macro_ptr = AstPtr::new(&e);
534 let mut ids = vec![]; 535 let mut ids = vec![];
535 self.collect_macro_call(e, syntax_ptr.clone(), true, |this, expansion| { 536 self.collect_macro_call(e, macro_ptr, true, |this, expansion| {
536 ids.push(match expansion { 537 ids.push(match expansion {
537 Some(it) => this.collect_expr(it), 538 Some(it) => this.collect_expr(it),
538 None => this.alloc_expr(Expr::Missing, syntax_ptr.clone()), 539 None => this.alloc_expr(Expr::Missing, syntax_ptr.clone()),
@@ -555,7 +556,7 @@ impl ExprCollector<'_> {
555 fn collect_macro_call<F: FnMut(&mut Self, Option<T>), T: ast::AstNode>( 556 fn collect_macro_call<F: FnMut(&mut Self, Option<T>), T: ast::AstNode>(
556 &mut self, 557 &mut self,
557 e: ast::MacroCall, 558 e: ast::MacroCall,
558 syntax_ptr: AstPtr<ast::Expr>, 559 syntax_ptr: AstPtr<ast::MacroCall>,
559 is_error_recoverable: bool, 560 is_error_recoverable: bool,
560 mut collector: F, 561 mut collector: F,
561 ) { 562 ) {
@@ -643,10 +644,14 @@ impl ExprCollector<'_> {
643 644
644 // Note that macro could be expended to multiple statements 645 // Note that macro could be expended to multiple statements
645 if let Some(ast::Expr::MacroCall(m)) = stmt.expr() { 646 if let Some(ast::Expr::MacroCall(m)) = stmt.expr() {
647 let macro_ptr = AstPtr::new(&m);
646 let syntax_ptr = AstPtr::new(&stmt.expr().unwrap()); 648 let syntax_ptr = AstPtr::new(&stmt.expr().unwrap());
647 649
648 self.collect_macro_call(m, syntax_ptr.clone(), false, |this, expansion| { 650 self.collect_macro_call(
649 match expansion { 651 m,
652 macro_ptr,
653 false,
654 |this, expansion| match expansion {
650 Some(expansion) => { 655 Some(expansion) => {
651 let statements: ast::MacroStmts = expansion; 656 let statements: ast::MacroStmts = expansion;
652 657
@@ -660,8 +665,8 @@ impl ExprCollector<'_> {
660 let expr = this.alloc_expr(Expr::Missing, syntax_ptr.clone()); 665 let expr = this.alloc_expr(Expr::Missing, syntax_ptr.clone());
661 this.statements_in_scope.push(Statement::Expr(expr)); 666 this.statements_in_scope.push(Statement::Expr(expr));
662 } 667 }
663 } 668 },
664 }); 669 );
665 } else { 670 } else {
666 let expr = self.collect_expr_opt(stmt.expr()); 671 let expr = self.collect_expr_opt(stmt.expr());
667 self.statements_in_scope.push(Statement::Expr(expr)); 672 self.statements_in_scope.push(Statement::Expr(expr));
@@ -848,8 +853,23 @@ impl ExprCollector<'_> {
848 Pat::Missing 853 Pat::Missing
849 } 854 }
850 } 855 }
856 ast::Pat::MacroPat(mac) => match mac.macro_call() {
857 Some(call) => {
858 let macro_ptr = AstPtr::new(&call);
859 let mut pat = None;
860 self.collect_macro_call(call, macro_ptr, true, |this, expanded_pat| {
861 pat = Some(this.collect_pat_opt(expanded_pat));
862 });
863
864 match pat {
865 Some(pat) => return pat,
866 None => Pat::Missing,
867 }
868 }
869 None => Pat::Missing,
870 },
851 // FIXME: implement 871 // FIXME: implement
852 ast::Pat::RangePat(_) | ast::Pat::MacroPat(_) => Pat::Missing, 872 ast::Pat::RangePat(_) => Pat::Missing,
853 }; 873 };
854 let ptr = AstPtr::new(&pat); 874 let ptr = AstPtr::new(&pat);
855 self.alloc_pat(pattern, Either::Left(ptr)) 875 self.alloc_pat(pattern, Either::Left(ptr))
diff --git a/crates/hir_def/src/item_tree.rs b/crates/hir_def/src/item_tree.rs
index 240662486..94e08f835 100644
--- a/crates/hir_def/src/item_tree.rs
+++ b/crates/hir_def/src/item_tree.rs
@@ -99,6 +99,11 @@ impl ItemTree {
99 // items. 99 // items.
100 ctx.lower_macro_stmts(stmts) 100 ctx.lower_macro_stmts(stmts)
101 }, 101 },
102 ast::Pat(_pat) => {
103 // FIXME: This occurs because macros in pattern position are treated as inner
104 // items and expanded during block DefMap computation
105 return Default::default();
106 },
102 ast::Expr(e) => { 107 ast::Expr(e) => {
103 // Macros can expand to expressions. We return an empty item tree in this case, but 108 // Macros can expand to expressions. We return an empty item tree in this case, but
104 // still need to collect inner items. 109 // still need to collect inner items.
diff --git a/crates/hir_def/src/item_tree/lower.rs b/crates/hir_def/src/item_tree/lower.rs
index c5629af24..45b099cf3 100644
--- a/crates/hir_def/src/item_tree/lower.rs
+++ b/crates/hir_def/src/item_tree/lower.rs
@@ -189,7 +189,7 @@ impl Ctx {
189 block_stack.push(self.source_ast_id_map.ast_id(&block)); 189 block_stack.push(self.source_ast_id_map.ast_id(&block));
190 }, 190 },
191 ast::Item(item) => { 191 ast::Item(item) => {
192 // FIXME: This triggers for macro calls in expression position 192 // FIXME: This triggers for macro calls in expression/pattern/type position
193 let mod_items = self.lower_mod_item(&item, true); 193 let mod_items = self.lower_mod_item(&item, true);
194 let current_block = block_stack.last(); 194 let current_block = block_stack.last();
195 if let (Some(mod_items), Some(block)) = (mod_items, current_block) { 195 if let (Some(mod_items), Some(block)) = (mod_items, current_block) {
diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs
index 10fe60821..ca705ee9d 100644
--- a/crates/hir_expand/src/db.rs
+++ b/crates/hir_expand/src/db.rs
@@ -439,6 +439,7 @@ fn to_fragment_kind(db: &dyn AstDatabase, id: MacroCallId) -> FragmentKind {
439 match parent.kind() { 439 match parent.kind() {
440 MACRO_ITEMS | SOURCE_FILE => FragmentKind::Items, 440 MACRO_ITEMS | SOURCE_FILE => FragmentKind::Items,
441 MACRO_STMTS => FragmentKind::Statements, 441 MACRO_STMTS => FragmentKind::Statements,
442 MACRO_PAT => FragmentKind::Pattern,
442 ITEM_LIST => FragmentKind::Items, 443 ITEM_LIST => FragmentKind::Items,
443 LET_STMT => { 444 LET_STMT => {
444 // FIXME: Handle LHS Pattern 445 // FIXME: Handle LHS Pattern
diff --git a/crates/hir_ty/src/tests/macros.rs b/crates/hir_ty/src/tests/macros.rs
index 86e3d8b86..b8e373ed8 100644
--- a/crates/hir_ty/src/tests/macros.rs
+++ b/crates/hir_ty/src/tests/macros.rs
@@ -1065,11 +1065,11 @@ fn macro_in_arm() {
1065 } 1065 }
1066 "#, 1066 "#,
1067 expect![[r#" 1067 expect![[r#"
1068 !0..2 '()': ()
1068 51..110 '{ ... }; }': () 1069 51..110 '{ ... }; }': ()
1069 61..62 'x': u32 1070 61..62 'x': u32
1070 65..107 'match ... }': u32 1071 65..107 'match ... }': u32
1071 71..73 '()': () 1072 71..73 '()': ()
1072 84..91 'unit!()': ()
1073 95..100 '92u32': u32 1073 95..100 '92u32': u32
1074 "#]], 1074 "#]],
1075 ); 1075 );
diff --git a/crates/hir_ty/src/tests/patterns.rs b/crates/hir_ty/src/tests/patterns.rs
index 85a28e76b..f514b3efe 100644
--- a/crates/hir_ty/src/tests/patterns.rs
+++ b/crates/hir_ty/src/tests/patterns.rs
@@ -1,6 +1,6 @@
1use expect_test::expect; 1use expect_test::expect;
2 2
3use super::{check_infer, check_infer_with_mismatches}; 3use super::{check_infer, check_infer_with_mismatches, check_types};
4 4
5#[test] 5#[test]
6fn infer_pattern() { 6fn infer_pattern() {
@@ -825,3 +825,29 @@ fn foo(foo: Foo) {
825 "#]], 825 "#]],
826 ); 826 );
827} 827}
828
829#[test]
830fn macro_pat() {
831 check_types(
832 r#"
833macro_rules! pat {
834 ($name:ident) => { Enum::Variant1($name) }
835}
836
837enum Enum {
838 Variant1(u8),
839 Variant2,
840}
841
842fn f(e: Enum) {
843 match e {
844 pat!(bind) => {
845 bind;
846 //^^^^ u8
847 }
848 Enum::Variant2 => {}
849 }
850}
851 "#,
852 )
853}
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs
index ca8ccb2da..d057d5402 100644
--- a/crates/ide/src/goto_definition.rs
+++ b/crates/ide/src/goto_definition.rs
@@ -1188,4 +1188,30 @@ pub fn gimme() -> theitem::TheItem {
1188"#, 1188"#,
1189 ); 1189 );
1190 } 1190 }
1191
1192 #[test]
1193 fn goto_ident_from_pat_macro() {
1194 check(
1195 r#"
1196macro_rules! pat {
1197 ($name:ident) => { Enum::Variant1($name) }
1198}
1199
1200enum Enum {
1201 Variant1(u8),
1202 Variant2,
1203}
1204
1205fn f(e: Enum) {
1206 match e {
1207 pat!(bind) => {
1208 //^^^^
1209 bind$0
1210 }
1211 Enum::Variant2 => {}
1212 }
1213}
1214"#,
1215 );
1216 }
1191} 1217}
diff --git a/crates/ide_assists/src/handlers/extract_function.rs b/crates/ide_assists/src/handlers/extract_function.rs
index 5fdc8bf38..af9543766 100644
--- a/crates/ide_assists/src/handlers/extract_function.rs
+++ b/crates/ide_assists/src/handlers/extract_function.rs
@@ -75,7 +75,8 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext) -> Option
75 let insert_after = scope_for_fn_insertion(&body, anchor)?; 75 let insert_after = scope_for_fn_insertion(&body, anchor)?;
76 let module = ctx.sema.scope(&insert_after).module()?; 76 let module = ctx.sema.scope(&insert_after).module()?;
77 77
78 let vars_defined_in_body_and_outlive = vars_defined_in_body_and_outlive(ctx, &body); 78 let vars_defined_in_body_and_outlive =
79 vars_defined_in_body_and_outlive(ctx, &body, &node.parent().as_ref().unwrap_or(&node));
79 let ret_ty = body_return_ty(ctx, &body)?; 80 let ret_ty = body_return_ty(ctx, &body)?;
80 81
81 // FIXME: we compute variables that outlive here just to check `never!` condition 82 // FIXME: we compute variables that outlive here just to check `never!` condition
@@ -257,7 +258,7 @@ struct Function {
257 control_flow: ControlFlow, 258 control_flow: ControlFlow,
258 ret_ty: RetType, 259 ret_ty: RetType,
259 body: FunctionBody, 260 body: FunctionBody,
260 vars_defined_in_body_and_outlive: Vec<Local>, 261 vars_defined_in_body_and_outlive: Vec<OutlivedLocal>,
261} 262}
262 263
263#[derive(Debug)] 264#[derive(Debug)]
@@ -296,9 +297,9 @@ impl Function {
296 RetType::Expr(ty) => FunType::Single(ty.clone()), 297 RetType::Expr(ty) => FunType::Single(ty.clone()),
297 RetType::Stmt => match self.vars_defined_in_body_and_outlive.as_slice() { 298 RetType::Stmt => match self.vars_defined_in_body_and_outlive.as_slice() {
298 [] => FunType::Unit, 299 [] => FunType::Unit,
299 [var] => FunType::Single(var.ty(ctx.db())), 300 [var] => FunType::Single(var.local.ty(ctx.db())),
300 vars => { 301 vars => {
301 let types = vars.iter().map(|v| v.ty(ctx.db())).collect(); 302 let types = vars.iter().map(|v| v.local.ty(ctx.db())).collect();
302 FunType::Tuple(types) 303 FunType::Tuple(types)
303 } 304 }
304 }, 305 },
@@ -562,6 +563,12 @@ impl HasTokenAtOffset for FunctionBody {
562 } 563 }
563} 564}
564 565
566#[derive(Debug)]
567struct OutlivedLocal {
568 local: Local,
569 mut_usage_outside_body: bool,
570}
571
565/// Try to guess what user wants to extract 572/// Try to guess what user wants to extract
566/// 573///
567/// We have basically have two cases: 574/// We have basically have two cases:
@@ -707,10 +714,10 @@ fn has_exclusive_usages(ctx: &AssistContext, usages: &LocalUsages, body: &Functi
707 .any(|reference| reference_is_exclusive(reference, body, ctx)) 714 .any(|reference| reference_is_exclusive(reference, body, ctx))
708} 715}
709 716
710/// checks if this reference requires `&mut` access inside body 717/// checks if this reference requires `&mut` access inside node
711fn reference_is_exclusive( 718fn reference_is_exclusive(
712 reference: &FileReference, 719 reference: &FileReference,
713 body: &FunctionBody, 720 node: &dyn HasTokenAtOffset,
714 ctx: &AssistContext, 721 ctx: &AssistContext,
715) -> bool { 722) -> bool {
716 // we directly modify variable with set: `n = 0`, `n += 1` 723 // we directly modify variable with set: `n = 0`, `n += 1`
@@ -719,7 +726,7 @@ fn reference_is_exclusive(
719 } 726 }
720 727
721 // we take `&mut` reference to variable: `&mut v` 728 // we take `&mut` reference to variable: `&mut v`
722 let path = match path_element_of_reference(body, reference) { 729 let path = match path_element_of_reference(node, reference) {
723 Some(path) => path, 730 Some(path) => path,
724 None => return false, 731 None => return false,
725 }; 732 };
@@ -820,10 +827,16 @@ fn vars_defined_in_body(body: &FunctionBody, ctx: &AssistContext) -> Vec<Local>
820} 827}
821 828
822/// list local variables defined inside `body` that should be returned from extracted function 829/// list local variables defined inside `body` that should be returned from extracted function
823fn vars_defined_in_body_and_outlive(ctx: &AssistContext, body: &FunctionBody) -> Vec<Local> { 830fn vars_defined_in_body_and_outlive(
824 let mut vars_defined_in_body = vars_defined_in_body(&body, ctx); 831 ctx: &AssistContext,
825 vars_defined_in_body.retain(|var| var_outlives_body(ctx, body, var)); 832 body: &FunctionBody,
833 parent: &SyntaxNode,
834) -> Vec<OutlivedLocal> {
835 let vars_defined_in_body = vars_defined_in_body(&body, ctx);
826 vars_defined_in_body 836 vars_defined_in_body
837 .into_iter()
838 .filter_map(|var| var_outlives_body(ctx, body, var, parent))
839 .collect()
827} 840}
828 841
829/// checks if the relevant local was defined before(outside of) body 842/// checks if the relevant local was defined before(outside of) body
@@ -843,11 +856,23 @@ fn either_syntax(value: &Either<ast::IdentPat, ast::SelfParam>) -> &SyntaxNode {
843 } 856 }
844} 857}
845 858
846/// checks if local variable is used after(outside of) body 859/// returns usage details if local variable is used after(outside of) body
847fn var_outlives_body(ctx: &AssistContext, body: &FunctionBody, var: &Local) -> bool { 860fn var_outlives_body(
848 let usages = LocalUsages::find(ctx, *var); 861 ctx: &AssistContext,
862 body: &FunctionBody,
863 var: Local,
864 parent: &SyntaxNode,
865) -> Option<OutlivedLocal> {
866 let usages = LocalUsages::find(ctx, var);
849 let has_usages = usages.iter().any(|reference| body.preceedes_range(reference.range)); 867 let has_usages = usages.iter().any(|reference| body.preceedes_range(reference.range));
850 has_usages 868 if !has_usages {
869 return None;
870 }
871 let has_mut_usages = usages
872 .iter()
873 .filter(|reference| body.preceedes_range(reference.range))
874 .any(|reference| reference_is_exclusive(reference, parent, ctx));
875 Some(OutlivedLocal { local: var, mut_usage_outside_body: has_mut_usages })
851} 876}
852 877
853fn body_return_ty(ctx: &AssistContext, body: &FunctionBody) -> Option<RetType> { 878fn body_return_ty(ctx: &AssistContext, body: &FunctionBody) -> Option<RetType> {
@@ -927,16 +952,25 @@ fn format_replacement(ctx: &AssistContext, fun: &Function, indent: IndentLevel)
927 let mut buf = String::new(); 952 let mut buf = String::new();
928 match fun.vars_defined_in_body_and_outlive.as_slice() { 953 match fun.vars_defined_in_body_and_outlive.as_slice() {
929 [] => {} 954 [] => {}
930 [var] => format_to!(buf, "let {} = ", var.name(ctx.db()).unwrap()), 955 [var] => {
956 format_to!(buf, "let {}{} = ", mut_modifier(var), var.local.name(ctx.db()).unwrap())
957 }
931 [v0, vs @ ..] => { 958 [v0, vs @ ..] => {
932 buf.push_str("let ("); 959 buf.push_str("let (");
933 format_to!(buf, "{}", v0.name(ctx.db()).unwrap()); 960 format_to!(buf, "{}{}", mut_modifier(v0), v0.local.name(ctx.db()).unwrap());
934 for var in vs { 961 for var in vs {
935 format_to!(buf, ", {}", var.name(ctx.db()).unwrap()); 962 format_to!(buf, ", {}{}", mut_modifier(var), var.local.name(ctx.db()).unwrap());
936 } 963 }
937 buf.push_str(") = "); 964 buf.push_str(") = ");
938 } 965 }
939 } 966 }
967 fn mut_modifier(var: &OutlivedLocal) -> &'static str {
968 if var.mut_usage_outside_body {
969 "mut "
970 } else {
971 ""
972 }
973 }
940 format_to!(buf, "{}", expr); 974 format_to!(buf, "{}", expr);
941 if fun.ret_ty.is_unit() 975 if fun.ret_ty.is_unit()
942 && (!fun.vars_defined_in_body_and_outlive.is_empty() || !expr.is_block_like()) 976 && (!fun.vars_defined_in_body_and_outlive.is_empty() || !expr.is_block_like())
@@ -1199,10 +1233,10 @@ fn make_body(
1199 match fun.vars_defined_in_body_and_outlive.as_slice() { 1233 match fun.vars_defined_in_body_and_outlive.as_slice() {
1200 [] => {} 1234 [] => {}
1201 [var] => { 1235 [var] => {
1202 tail_expr = Some(path_expr_from_local(ctx, *var)); 1236 tail_expr = Some(path_expr_from_local(ctx, var.local));
1203 } 1237 }
1204 vars => { 1238 vars => {
1205 let exprs = vars.iter().map(|var| path_expr_from_local(ctx, *var)); 1239 let exprs = vars.iter().map(|var| path_expr_from_local(ctx, var.local));
1206 let expr = make::expr_tuple(exprs); 1240 let expr = make::expr_tuple(exprs);
1207 tail_expr = Some(expr); 1241 tail_expr = Some(expr);
1208 } 1242 }
@@ -2111,6 +2145,30 @@ fn $0fun_name(n: i32) -> i32 {
2111 } 2145 }
2112 2146
2113 #[test] 2147 #[test]
2148 fn variable_defined_inside_and_used_after_mutably_no_ret() {
2149 check_assist(
2150 extract_function,
2151 r"
2152fn foo() {
2153 let n = 1;
2154 $0let mut k = n * n;$0
2155 k += 1;
2156}",
2157 r"
2158fn foo() {
2159 let n = 1;
2160 let mut k = fun_name(n);
2161 k += 1;
2162}
2163
2164fn $0fun_name(n: i32) -> i32 {
2165 let mut k = n * n;
2166 k
2167}",
2168 );
2169 }
2170
2171 #[test]
2114 fn two_variables_defined_inside_and_used_after_no_ret() { 2172 fn two_variables_defined_inside_and_used_after_no_ret() {
2115 check_assist( 2173 check_assist(
2116 extract_function, 2174 extract_function,
@@ -2137,6 +2195,38 @@ fn $0fun_name(n: i32) -> (i32, i32) {
2137 } 2195 }
2138 2196
2139 #[test] 2197 #[test]
2198 fn multi_variables_defined_inside_and_used_after_mutably_no_ret() {
2199 check_assist(
2200 extract_function,
2201 r"
2202fn foo() {
2203 let n = 1;
2204 $0let mut k = n * n;
2205 let mut m = k + 2;
2206 let mut o = m + 3;
2207 o += 1;$0
2208 k += o;
2209 m = 1;
2210}",
2211 r"
2212fn foo() {
2213 let n = 1;
2214 let (mut k, mut m, o) = fun_name(n);
2215 k += o;
2216 m = 1;
2217}
2218
2219fn $0fun_name(n: i32) -> (i32, i32, i32) {
2220 let mut k = n * n;
2221 let mut m = k + 2;
2222 let mut o = m + 3;
2223 o += 1;
2224 (k, m, o)
2225}",
2226 );
2227 }
2228
2229 #[test]
2140 fn nontrivial_patterns_define_variables() { 2230 fn nontrivial_patterns_define_variables() {
2141 check_assist( 2231 check_assist(
2142 extract_function, 2232 extract_function,