diff options
-rw-r--r-- | crates/hir_def/src/body/lower.rs | 34 | ||||
-rw-r--r-- | crates/hir_def/src/item_tree.rs | 5 | ||||
-rw-r--r-- | crates/hir_def/src/item_tree/lower.rs | 2 | ||||
-rw-r--r-- | crates/hir_expand/src/db.rs | 1 | ||||
-rw-r--r-- | crates/hir_ty/src/tests/macros.rs | 2 | ||||
-rw-r--r-- | crates/hir_ty/src/tests/patterns.rs | 28 | ||||
-rw-r--r-- | crates/ide/src/goto_definition.rs | 26 | ||||
-rw-r--r-- | crates/ide_assists/src/handlers/extract_function.rs | 128 |
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 @@ | |||
1 | use expect_test::expect; | 1 | use expect_test::expect; |
2 | 2 | ||
3 | use super::{check_infer, check_infer_with_mismatches}; | 3 | use super::{check_infer, check_infer_with_mismatches, check_types}; |
4 | 4 | ||
5 | #[test] | 5 | #[test] |
6 | fn infer_pattern() { | 6 | fn infer_pattern() { |
@@ -825,3 +825,29 @@ fn foo(foo: Foo) { | |||
825 | "#]], | 825 | "#]], |
826 | ); | 826 | ); |
827 | } | 827 | } |
828 | |||
829 | #[test] | ||
830 | fn macro_pat() { | ||
831 | check_types( | ||
832 | r#" | ||
833 | macro_rules! pat { | ||
834 | ($name:ident) => { Enum::Variant1($name) } | ||
835 | } | ||
836 | |||
837 | enum Enum { | ||
838 | Variant1(u8), | ||
839 | Variant2, | ||
840 | } | ||
841 | |||
842 | fn 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#" | ||
1196 | macro_rules! pat { | ||
1197 | ($name:ident) => { Enum::Variant1($name) } | ||
1198 | } | ||
1199 | |||
1200 | enum Enum { | ||
1201 | Variant1(u8), | ||
1202 | Variant2, | ||
1203 | } | ||
1204 | |||
1205 | fn 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)] | ||
567 | struct 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 |
711 | fn reference_is_exclusive( | 718 | fn 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 |
823 | fn vars_defined_in_body_and_outlive(ctx: &AssistContext, body: &FunctionBody) -> Vec<Local> { | 830 | fn 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 |
847 | fn var_outlives_body(ctx: &AssistContext, body: &FunctionBody, var: &Local) -> bool { | 860 | fn 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 | ||
853 | fn body_return_ty(ctx: &AssistContext, body: &FunctionBody) -> Option<RetType> { | 878 | fn 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" | ||
2152 | fn foo() { | ||
2153 | let n = 1; | ||
2154 | $0let mut k = n * n;$0 | ||
2155 | k += 1; | ||
2156 | }", | ||
2157 | r" | ||
2158 | fn foo() { | ||
2159 | let n = 1; | ||
2160 | let mut k = fun_name(n); | ||
2161 | k += 1; | ||
2162 | } | ||
2163 | |||
2164 | fn $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" | ||
2202 | fn 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" | ||
2212 | fn foo() { | ||
2213 | let n = 1; | ||
2214 | let (mut k, mut m, o) = fun_name(n); | ||
2215 | k += o; | ||
2216 | m = 1; | ||
2217 | } | ||
2218 | |||
2219 | fn $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, |