1use std::{iter, ops::RangeInclusive};
2
3use ast::make;
4use either::Either;
5use hir::{
6 HasSource, HirDisplay, InFile, Local, LocalSource, ModuleDef, PathResolution, Semantics,
7 TypeInfo, TypeParam,
8};
9use ide_db::{
10 FxIndexSet, RootDatabase,
11 assists::GroupLabel,
12 defs::Definition,
13 famous_defs::FamousDefs,
14 helpers::mod_path_to_ast,
15 imports::insert_use::{ImportScope, insert_use},
16 search::{FileReference, ReferenceCategory, SearchScope},
17 source_change::SourceChangeBuilder,
18 syntax_helpers::node_ext::{
19 for_each_tail_expr, preorder_expr, walk_pat, walk_patterns_in_expr,
20 },
21};
22use itertools::Itertools;
23use syntax::{
24 Edition, SyntaxElement,
25 SyntaxKind::{self, COMMENT},
26 SyntaxNode, SyntaxToken, T, TextRange, TextSize, TokenAtOffset, WalkEvent,
27 ast::{
28 self, AstNode, AstToken, HasGenericParams, HasName, edit::IndentLevel,
29 edit_in_place::Indent,
30 },
31 match_ast, ted,
32};
33
34use crate::{
35 AssistId,
36 assist_context::{AssistContext, Assists, TreeMutator},
37 utils::generate_impl,
38};
39
40pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
68 let range = ctx.selection_trimmed();
69 if range.is_empty() {
70 return None;
71 }
72
73 let node = ctx.covering_element();
74 if matches!(node.kind(), T!['{'] | T!['}'] | T!['('] | T![')'] | T!['['] | T![']']) {
75 cov_mark::hit!(extract_function_in_braces_is_not_applicable);
76 return None;
77 }
78
79 if node.kind() == COMMENT {
80 cov_mark::hit!(extract_function_in_comment_is_not_applicable);
81 return None;
82 }
83
84 let node = match node {
85 syntax::NodeOrToken::Node(n) => n,
86 syntax::NodeOrToken::Token(t) => t.parent()?,
87 };
88
89 let body = extraction_target(&node, range)?;
90
91 let (locals_used, self_param) = body.analyze(&ctx.sema);
92
93 let anchor = if self_param.is_some() { Anchor::Method } else { Anchor::Freestanding };
94 let insert_after = node_to_insert_after(&body, anchor)?;
95 let semantics_scope = ctx.sema.scope(&insert_after)?;
96 let module = semantics_scope.module();
97 let edition = semantics_scope.krate().edition(ctx.db());
98
99 let (container_info, contains_tail_expr) = body.analyze_container(&ctx.sema, edition)?;
100
101 let ret_ty = body.return_ty(ctx)?;
102 let control_flow = body.external_control_flow(ctx, &container_info)?;
103 let ret_values = body.ret_values(ctx, node.parent().as_ref().unwrap_or(&node));
104
105 let target_range = body.text_range();
106
107 let scope = ImportScope::find_insert_use_container(&node, &ctx.sema)?;
108
109 acc.add_group(
110 &GroupLabel("Extract into...".to_owned()),
111 AssistId::refactor_extract("extract_function"),
112 "Extract into function",
113 target_range,
114 move |builder| {
115 let outliving_locals: Vec<_> = ret_values.collect();
116 if stdx::never!(!outliving_locals.is_empty() && !ret_ty.is_unit()) {
117 return;
119 }
120
121 let params = body.extracted_function_params(ctx, &container_info, locals_used);
122
123 let name = make_function_name(&semantics_scope);
124
125 let fun = Function {
126 name,
127 self_param,
128 params,
129 control_flow,
130 ret_ty,
131 body,
132 outliving_locals,
133 contains_tail_expr,
134 mods: container_info,
135 };
136
137 let new_indent = IndentLevel::from_node(&insert_after);
138 let old_indent = fun.body.indent_level();
139
140 let insert_after = builder.make_syntax_mut(insert_after);
141
142 let call_expr = make_call(ctx, &fun, old_indent);
143
144 let elements = match &fun.body {
146 FunctionBody::Expr(expr) => {
147 let expr = &builder.make_mut(expr.clone());
149 let node = SyntaxElement::Node(expr.syntax().clone());
150
151 node.clone()..=node
152 }
153 FunctionBody::Span { parent, elements, .. } => {
154 let parent = builder.make_mut(parent.clone());
156
157 let start = parent
158 .syntax()
159 .children_with_tokens()
160 .nth(elements.start().index())
161 .expect("should be able to find mutable start element");
162
163 let end = parent
164 .syntax()
165 .children_with_tokens()
166 .nth(elements.end().index())
167 .expect("should be able to find mutable end element");
168
169 start..=end
170 }
171 };
172
173 let has_impl_wrapper =
174 insert_after.ancestors().any(|a| a.kind() == SyntaxKind::IMPL && a != insert_after);
175
176 let fn_def = format_function(ctx, module, &fun, old_indent).clone_for_update();
177
178 if let Some(cap) = ctx.config.snippet_cap
179 && let Some(name) = fn_def.name()
180 {
181 builder.add_tabstop_before(cap, name);
182 }
183
184 let fn_def = match fun.self_param_adt(ctx) {
185 Some(adt) if anchor == Anchor::Method && !has_impl_wrapper => {
186 fn_def.indent(1.into());
187
188 let impl_ = generate_impl(&adt);
189 impl_.indent(new_indent);
190 impl_.get_or_create_assoc_item_list().add_item(fn_def.into());
191
192 impl_.syntax().clone()
193 }
194 _ => {
195 fn_def.indent(new_indent);
196
197 fn_def.syntax().clone()
198 }
199 };
200
201 if fun
203 .control_flow
204 .kind
205 .is_some_and(|kind| matches!(kind, FlowKind::Break(_, _) | FlowKind::Continue(_)))
206 {
207 let scope = builder.make_import_scope_mut(scope);
208 let control_flow_enum =
209 FamousDefs(&ctx.sema, module.krate(ctx.db())).core_ops_ControlFlow();
210
211 if let Some(control_flow_enum) = control_flow_enum {
212 let cfg =
213 ctx.config.find_path_config(ctx.sema.is_nightly(module.krate(ctx.sema.db)));
214 let mod_path = module.find_use_path(
215 ctx.sema.db,
216 ModuleDef::from(control_flow_enum),
217 ctx.config.insert_use.prefix_kind,
218 cfg,
219 );
220
221 if let Some(mod_path) = mod_path {
222 insert_use(
223 &scope,
224 mod_path_to_ast(&mod_path, edition),
225 &ctx.config.insert_use,
226 );
227 }
228 }
229 }
230
231 fixup_call_site(builder, &fun.body);
233 ted::replace_all(elements, vec![call_expr.into()]);
234
235 ted::insert_all_raw(
237 ted::Position::after(insert_after),
238 vec![make::tokens::whitespace(&format!("\n\n{new_indent}")).into(), fn_def.into()],
239 );
240 },
241 )
242}
243
244fn make_function_name(semantics_scope: &hir::SemanticsScope<'_>) -> ast::NameRef {
245 let mut names_in_scope = vec![];
246 semantics_scope.process_all_names(&mut |name, _| {
247 names_in_scope.push(
248 name.display(semantics_scope.db, semantics_scope.krate().edition(semantics_scope.db))
249 .to_string(),
250 )
251 });
252
253 let default_name = "fun_name";
254
255 let mut name = default_name.to_owned();
256 let mut counter = 0;
257 while names_in_scope.contains(&name) {
258 counter += 1;
259 name = format!("{default_name}{counter}")
260 }
261 make::name_ref(&name)
262}
263
264fn extraction_target(node: &SyntaxNode, selection_range: TextRange) -> Option<FunctionBody> {
282 if let Some(stmt) = ast::Stmt::cast(node.clone()) {
283 return match stmt {
284 ast::Stmt::Item(_) => None,
285 ast::Stmt::ExprStmt(_) | ast::Stmt::LetStmt(_) => FunctionBody::from_range(
286 node.parent().and_then(ast::StmtList::cast)?,
287 node.text_range(),
288 ),
289 };
290 }
291
292 if let Some(stmt_list) = ast::StmtList::cast(node.clone()) {
294 if let Some(block_expr) = stmt_list.syntax().parent().and_then(ast::BlockExpr::cast)
295 && block_expr.syntax().text_range() == selection_range
296 {
297 return FunctionBody::from_expr(block_expr.into());
298 }
299
300 return FunctionBody::from_range(stmt_list, selection_range);
302 }
303
304 let expr = ast::Expr::cast(node.clone())?;
305 if node.text_range() == selection_range {
307 return FunctionBody::from_expr(expr);
308 }
309
310 node.ancestors().find_map(ast::Expr::cast).and_then(FunctionBody::from_expr)
311}
312
313#[derive(Debug)]
314struct Function<'db> {
315 name: ast::NameRef,
316 self_param: Option<ast::SelfParam>,
317 params: Vec<Param<'db>>,
318 control_flow: ControlFlow<'db>,
319 ret_ty: RetType<'db>,
320 body: FunctionBody,
321 outliving_locals: Vec<OutlivedLocal>,
322 contains_tail_expr: bool,
324 mods: ContainerInfo<'db>,
325}
326
327#[derive(Debug)]
328struct Param<'db> {
329 var: Local,
330 ty: hir::Type<'db>,
331 move_local: bool,
332 requires_mut: bool,
333 is_copy: bool,
334}
335
336#[derive(Debug, Clone, Copy, PartialEq, Eq)]
337enum ParamKind {
338 Value,
339 MutValue,
340 SharedRef,
341 MutRef,
342}
343
344#[derive(Debug)]
345enum FunType<'db> {
346 Unit,
347 Single(hir::Type<'db>),
348 Tuple(Vec<hir::Type<'db>>),
349}
350
351#[derive(Debug, Eq, PartialEq, Clone, Copy)]
353enum Anchor {
354 Freestanding,
356 Method,
358}
359
360#[derive(Debug)]
363struct ControlFlow<'db> {
364 kind: Option<FlowKind<'db>>,
365 is_async: bool,
366 is_unsafe: bool,
367}
368
369#[derive(Clone, Debug)]
371struct ContainerInfo<'db> {
372 is_const: bool,
373 parent_loop: Option<SyntaxNode>,
374 ret_type: Option<hir::Type<'db>>,
376 generic_param_lists: Vec<ast::GenericParamList>,
377 where_clauses: Vec<ast::WhereClause>,
378 edition: Edition,
379}
380
381#[derive(Debug, Clone)]
394enum FlowKind<'db> {
395 Return(Option<ast::Expr>),
397 Try {
398 kind: TryKind<'db>,
399 },
400 Break(Option<ast::Lifetime>, Option<ast::Expr>),
402 Continue(Option<ast::Lifetime>),
404}
405
406#[derive(Debug, Clone)]
407enum TryKind<'db> {
408 Option,
409 Result { ty: hir::Type<'db> },
410}
411
412#[derive(Debug)]
413enum RetType<'db> {
414 Expr(hir::Type<'db>),
415 Stmt,
416}
417
418impl RetType<'_> {
419 fn is_unit(&self) -> bool {
420 match self {
421 RetType::Expr(ty) => ty.is_unit(),
422 RetType::Stmt => true,
423 }
424 }
425}
426
427#[derive(Debug)]
430enum FunctionBody {
431 Expr(ast::Expr),
432 Span { parent: ast::StmtList, elements: RangeInclusive<SyntaxElement>, text_range: TextRange },
433}
434
435#[derive(Debug)]
436struct OutlivedLocal {
437 local: Local,
438 mut_usage_outside_body: bool,
439}
440
441struct LocalUsages(ide_db::search::UsageSearchResult);
445
446impl LocalUsages {
447 fn find_local_usages(ctx: &AssistContext<'_>, var: Local) -> Self {
448 Self(
449 Definition::Local(var)
450 .usages(&ctx.sema)
451 .in_scope(&SearchScope::single_file(ctx.file_id()))
452 .all(),
453 )
454 }
455
456 fn iter(&self) -> impl Iterator<Item = &FileReference> + '_ {
457 self.0.iter().flat_map(|(_, rs)| rs)
458 }
459}
460
461impl<'db> Function<'db> {
462 fn return_type(&self, ctx: &AssistContext<'db>) -> FunType<'db> {
463 match &self.ret_ty {
464 RetType::Expr(ty) if ty.is_unit() => FunType::Unit,
465 RetType::Expr(ty) => FunType::Single(ty.clone()),
466 RetType::Stmt => match self.outliving_locals.as_slice() {
467 [] => FunType::Unit,
468 [var] => FunType::Single(var.local.ty(ctx.db())),
469 vars => {
470 let types = vars.iter().map(|v| v.local.ty(ctx.db())).collect();
471 FunType::Tuple(types)
472 }
473 },
474 }
475 }
476
477 fn self_param_adt(&self, ctx: &AssistContext<'_>) -> Option<ast::Adt> {
478 let self_param = self.self_param.as_ref()?;
479 let def = ctx.sema.to_def(self_param)?;
480 let adt = def.ty(ctx.db()).strip_references().as_adt()?;
481 let InFile { file_id: _, value } = adt.source(ctx.db())?;
482 Some(value)
483 }
484}
485
486impl ParamKind {
487 fn is_ref(&self) -> bool {
488 matches!(self, ParamKind::SharedRef | ParamKind::MutRef)
489 }
490}
491
492impl<'db> Param<'db> {
493 fn kind(&self) -> ParamKind {
494 match (self.move_local, self.requires_mut, self.is_copy) {
495 (false, true, _) => ParamKind::MutRef,
496 (false, false, false) => ParamKind::SharedRef,
497 (true, true, _) => ParamKind::MutValue,
498 (_, false, _) => ParamKind::Value,
499 }
500 }
501
502 fn to_arg(&self, ctx: &AssistContext<'db>, edition: Edition) -> ast::Expr {
503 let var = path_expr_from_local(ctx, self.var, edition);
504 match self.kind() {
505 ParamKind::Value | ParamKind::MutValue => var,
506 ParamKind::SharedRef => make::expr_ref(var, false),
507 ParamKind::MutRef => make::expr_ref(var, true),
508 }
509 }
510
511 fn to_param(
512 &self,
513 ctx: &AssistContext<'_>,
514 module: hir::Module,
515 edition: Edition,
516 ) -> ast::Param {
517 let var = self.var.name(ctx.db()).display(ctx.db(), edition).to_string();
518 let var_name = make::name(&var);
519 let pat = match self.kind() {
520 ParamKind::MutValue => make::ident_pat(false, true, var_name),
521 ParamKind::Value | ParamKind::SharedRef | ParamKind::MutRef => {
522 make::ext::simple_ident_pat(var_name)
523 }
524 };
525
526 let ty = make_ty(&self.ty, ctx, module);
527 let ty = match self.kind() {
528 ParamKind::Value | ParamKind::MutValue => ty,
529 ParamKind::SharedRef => make::ty_ref(ty, false),
530 ParamKind::MutRef => make::ty_ref(ty, true),
531 };
532
533 make::param(pat.into(), ty)
534 }
535}
536
537impl<'db> TryKind<'db> {
538 fn of_ty(
539 ty: hir::Type<'db>,
540 ctx: &AssistContext<'db>,
541 edition: Edition,
542 ) -> Option<TryKind<'db>> {
543 if ty.is_unknown() {
544 return Some(TryKind::Result { ty });
546 }
547 let adt = ty.as_adt()?;
548 let name = adt.name(ctx.db());
549 let name = &name.display(ctx.db(), edition).to_string();
552 match name.as_str() {
553 "Option" => Some(TryKind::Option),
554 "Result" => Some(TryKind::Result { ty }),
555 _ => None,
556 }
557 }
558}
559
560impl<'db> FlowKind<'db> {
561 fn make_result_handler(&self, expr: Option<ast::Expr>) -> ast::Expr {
562 match self {
563 FlowKind::Return(_) => make::expr_return(expr),
564 FlowKind::Break(label, _) => make::expr_break(label.clone(), expr),
565 FlowKind::Try { .. } => {
566 stdx::never!("cannot have result handler with try");
567 expr.unwrap_or_else(|| make::expr_return(None))
568 }
569 FlowKind::Continue(label) => {
570 stdx::always!(expr.is_none(), "continue with value is not possible");
571 make::expr_continue(label.clone())
572 }
573 }
574 }
575
576 fn expr_ty(&self, ctx: &AssistContext<'db>) -> Option<hir::Type<'db>> {
577 match self {
578 FlowKind::Return(Some(expr)) | FlowKind::Break(_, Some(expr)) => {
579 ctx.sema.type_of_expr(expr).map(TypeInfo::adjusted)
580 }
581 FlowKind::Try { .. } => {
582 stdx::never!("try does not have defined expr_ty");
583 None
584 }
585 _ => None,
586 }
587 }
588}
589
590impl FunctionBody {
591 fn parent(&self) -> Option<SyntaxNode> {
592 match self {
593 FunctionBody::Expr(expr) => expr.syntax().parent(),
594 FunctionBody::Span { parent, .. } => Some(parent.syntax().clone()),
595 }
596 }
597
598 fn node(&self) -> &SyntaxNode {
599 match self {
600 FunctionBody::Expr(e) => e.syntax(),
601 FunctionBody::Span { parent, .. } => parent.syntax(),
602 }
603 }
604
605 fn extracted_from_trait_impl(&self) -> bool {
606 match self.node().ancestors().find_map(ast::Impl::cast) {
607 Some(c) => c.trait_().is_some(),
608 None => false,
609 }
610 }
611
612 fn descendants(&self) -> impl Iterator<Item = SyntaxNode> {
613 match self {
614 FunctionBody::Expr(expr) => expr.syntax().descendants(),
615 FunctionBody::Span { parent, .. } => parent.syntax().descendants(),
616 }
617 }
618
619 fn descendant_paths(&self) -> impl Iterator<Item = ast::Path> {
620 self.descendants().filter_map(|node| {
621 match_ast! {
622 match node {
623 ast::Path(it) => Some(it),
624 _ => None
625 }
626 }
627 })
628 }
629
630 fn from_expr(expr: ast::Expr) -> Option<Self> {
631 match expr {
632 ast::Expr::BreakExpr(it) => it.expr().map(Self::Expr),
633 ast::Expr::ReturnExpr(it) => it.expr().map(Self::Expr),
634 ast::Expr::BlockExpr(it) if !it.is_standalone() => None,
635 expr => Some(Self::Expr(expr)),
636 }
637 }
638
639 fn from_range(parent: ast::StmtList, selected: TextRange) -> Option<FunctionBody> {
640 let full_body = parent.syntax().children_with_tokens();
641
642 let mut stmts_in_selection = full_body
644 .filter(|it| ast::Stmt::can_cast(it.kind()) || it.kind() == COMMENT)
645 .filter(|it| selected.intersect(it.text_range()).filter(|it| !it.is_empty()).is_some());
646
647 let first_element = stmts_in_selection.next();
648
649 let last_element = if let Some(tail_expr) =
652 parent.tail_expr().filter(|it| selected.intersect(it.syntax().text_range()).is_some())
653 {
654 Some(tail_expr.syntax().clone().into())
655 } else {
656 stmts_in_selection.last()
657 };
658
659 let elements = match (first_element, last_element) {
660 (None, _) => {
661 cov_mark::hit!(extract_function_empty_selection_is_not_applicable);
662 return None;
663 }
664 (Some(first), None) => first.clone()..=first,
665 (Some(first), Some(last)) => first..=last,
666 };
667
668 let text_range = elements.start().text_range().cover(elements.end().text_range());
669
670 Some(Self::Span { parent, elements, text_range })
671 }
672
673 fn indent_level(&self) -> IndentLevel {
674 match &self {
675 FunctionBody::Expr(expr) => IndentLevel::from_node(expr.syntax()),
676 FunctionBody::Span { parent, .. } => IndentLevel::from_node(parent.syntax()) + 1,
677 }
678 }
679
680 fn tail_expr(&self) -> Option<ast::Expr> {
681 match &self {
682 FunctionBody::Expr(expr) => Some(expr.clone()),
683 FunctionBody::Span { parent, text_range, .. } => {
684 let tail_expr = parent.tail_expr()?;
685 text_range.contains_range(tail_expr.syntax().text_range()).then_some(tail_expr)
686 }
687 }
688 }
689
690 fn preorder_expr(&self, cb: &mut dyn FnMut(WalkEvent<ast::Expr>) -> bool) {
691 match self {
692 FunctionBody::Expr(expr) => preorder_expr(expr, cb),
693 FunctionBody::Span { parent, text_range, .. } => {
694 parent
695 .statements()
696 .filter(|stmt| text_range.contains_range(stmt.syntax().text_range()))
697 .filter_map(|stmt| match stmt {
698 ast::Stmt::ExprStmt(expr_stmt) => expr_stmt.expr().map(|e| vec![e]),
699 ast::Stmt::Item(_) => None,
700 ast::Stmt::LetStmt(stmt) => {
701 let init = stmt.initializer();
702 let let_else = stmt
703 .let_else()
704 .and_then(|le| le.block_expr())
705 .map(ast::Expr::BlockExpr);
706
707 match (init, let_else) {
708 (Some(i), Some(le)) => Some(vec![i, le]),
709 (Some(i), _) => Some(vec![i]),
710 (_, Some(le)) => Some(vec![le]),
711 _ => None,
712 }
713 }
714 })
715 .flatten()
716 .for_each(|expr| preorder_expr(&expr, cb));
717 if let Some(expr) = parent
718 .tail_expr()
719 .filter(|it| text_range.contains_range(it.syntax().text_range()))
720 {
721 preorder_expr(&expr, cb);
722 }
723 }
724 }
725 }
726
727 fn walk_pat(&self, cb: &mut dyn FnMut(ast::Pat)) {
728 match self {
729 FunctionBody::Expr(expr) => walk_patterns_in_expr(expr, cb),
730 FunctionBody::Span { parent, text_range, .. } => {
731 parent
732 .statements()
733 .filter(|stmt| text_range.contains_range(stmt.syntax().text_range()))
734 .for_each(|stmt| match stmt {
735 ast::Stmt::ExprStmt(expr_stmt) => {
736 if let Some(expr) = expr_stmt.expr() {
737 walk_patterns_in_expr(&expr, cb)
738 }
739 }
740 ast::Stmt::Item(_) => (),
741 ast::Stmt::LetStmt(stmt) => {
742 if let Some(pat) = stmt.pat() {
743 _ = walk_pat(&pat, &mut |pat| {
744 cb(pat);
745 std::ops::ControlFlow::<(), ()>::Continue(())
746 });
747 }
748 if let Some(expr) = stmt.initializer() {
749 walk_patterns_in_expr(&expr, cb);
750 }
751 }
752 });
753 if let Some(expr) = parent
754 .tail_expr()
755 .filter(|it| text_range.contains_range(it.syntax().text_range()))
756 {
757 walk_patterns_in_expr(&expr, cb);
758 }
759 }
760 }
761 }
762
763 fn text_range(&self) -> TextRange {
764 match self {
765 FunctionBody::Expr(expr) => expr.syntax().text_range(),
766 &FunctionBody::Span { text_range, .. } => text_range,
767 }
768 }
769
770 fn contains_range(&self, range: TextRange) -> bool {
771 self.text_range().contains_range(range)
772 }
773
774 fn precedes_range(&self, range: TextRange) -> bool {
775 self.text_range().end() <= range.start()
776 }
777
778 fn contains_node(&self, node: &SyntaxNode) -> bool {
779 self.contains_range(node.text_range())
780 }
781}
782
783impl FunctionBody {
784 fn analyze(
787 &self,
788 sema: &Semantics<'_, RootDatabase>,
789 ) -> (FxIndexSet<Local>, Option<ast::SelfParam>) {
790 let mut self_param = None;
791 let mut res = FxIndexSet::default();
792
793 let (text_range, element) = match self {
794 FunctionBody::Expr(expr) => (expr.syntax().text_range(), Either::Left(expr)),
795 FunctionBody::Span { parent, text_range, .. } => (*text_range, Either::Right(parent)),
796 };
797
798 let mut add_name_if_local = |local_ref: Local| {
799 let InFile { file_id, value } = local_ref.primary_source(sema.db).source;
801 if !file_id.is_macro() {
802 match value {
803 Either::Right(it) => {
804 self_param.replace(it);
805 }
806 Either::Left(_) => {
807 res.insert(local_ref);
808 }
809 }
810 }
811 };
812
813 if let Some(locals) = sema.locals_used(element, text_range) {
814 locals.into_iter().for_each(&mut add_name_if_local);
815 }
816
817 (res, self_param)
818 }
819
820 fn analyze_container<'db>(
821 &self,
822 sema: &Semantics<'db, RootDatabase>,
823 edition: Edition,
824 ) -> Option<(ContainerInfo<'db>, bool)> {
825 let mut ancestors = self.parent()?.ancestors();
826 let infer_expr_opt = |expr| sema.type_of_expr(&expr?).map(TypeInfo::adjusted);
827 let mut parent_loop = None;
828 let mut set_parent_loop = |loop_: &dyn ast::HasLoopBody| {
829 if loop_
830 .loop_body()
831 .is_some_and(|it| it.syntax().text_range().contains_range(self.text_range()))
832 {
833 parent_loop.get_or_insert(loop_.syntax().clone());
834 }
835 };
836
837 let (is_const, expr, ty) = loop {
838 let anc = ancestors.next()?;
839 break match_ast! {
840 match anc {
841 ast::ClosureExpr(closure) => (false, closure.body(), infer_expr_opt(closure.body())),
842 ast::BlockExpr(block_expr) => {
843 let (constness, block) = match block_expr.modifier() {
844 Some(ast::BlockModifier::Const(_)) => (true, block_expr),
845 Some(ast::BlockModifier::Try(_)) => (false, block_expr),
846 Some(ast::BlockModifier::Label(label)) if label.lifetime().is_some() => (false, block_expr),
847 _ => continue,
848 };
849 let expr = Some(ast::Expr::BlockExpr(block));
850 (constness, expr.clone(), infer_expr_opt(expr))
851 },
852 ast::Fn(fn_) => {
853 let func = sema.to_def(&fn_)?;
854 let mut ret_ty = func.ret_type(sema.db);
855 if func.is_async(sema.db)
856 && let Some(async_ret) = func.async_ret_type(sema.db) {
857 ret_ty = async_ret;
858 }
859 (fn_.const_token().is_some(), fn_.body().map(ast::Expr::BlockExpr), Some(ret_ty))
860 },
861 ast::Static(statik) => {
862 (true, statik.body(), Some(sema.to_def(&statik)?.ty(sema.db)))
863 },
864 ast::ConstArg(ca) => {
865 (true, ca.expr(), infer_expr_opt(ca.expr()))
866 },
867 ast::Const(konst) => {
868 (true, konst.body(), Some(sema.to_def(&konst)?.ty(sema.db)))
869 },
870 ast::ConstParam(cp) => {
871 (true, cp.default_val()?.expr(), Some(sema.to_def(&cp)?.ty(sema.db)))
872 },
873 ast::ConstBlockPat(cbp) => {
874 let expr = cbp.block_expr().map(ast::Expr::BlockExpr);
875 (true, expr.clone(), infer_expr_opt(expr))
876 },
877 ast::Variant(__) => return None,
878 ast::Meta(__) => return None,
879 ast::LoopExpr(it) => {
880 set_parent_loop(&it);
881 continue;
882 },
883 ast::ForExpr(it) => {
884 set_parent_loop(&it);
885 continue;
886 },
887 ast::WhileExpr(it) => {
888 set_parent_loop(&it);
889 continue;
890 },
891 _ => continue,
892 }
893 };
894 };
895
896 let expr = expr?;
897 let contains_tail_expr = if let Some(body_tail) = self.tail_expr() {
898 let mut contains_tail_expr = false;
899 let tail_expr_range = body_tail.syntax().text_range();
900 for_each_tail_expr(&expr, &mut |e| {
901 if tail_expr_range.contains_range(e.syntax().text_range()) {
902 contains_tail_expr = true;
903 }
904 });
905 contains_tail_expr
906 } else {
907 false
908 };
909
910 let parent = self.parent()?;
911 let parents = generic_parents(&parent);
912 let generic_param_lists = parents.iter().filter_map(|it| it.generic_param_list()).collect();
913 let where_clauses = parents.iter().filter_map(|it| it.where_clause()).collect();
914
915 Some((
916 ContainerInfo {
917 is_const,
918 parent_loop,
919 ret_type: ty,
920 generic_param_lists,
921 where_clauses,
922 edition,
923 },
924 contains_tail_expr,
925 ))
926 }
927
928 fn return_ty<'db>(&self, ctx: &AssistContext<'db>) -> Option<RetType<'db>> {
929 match self.tail_expr() {
930 Some(expr) => ctx.sema.type_of_expr(&expr).map(TypeInfo::original).map(RetType::Expr),
931 None => Some(RetType::Stmt),
932 }
933 }
934
935 fn ret_values<'a>(
937 &self,
938 ctx: &'a AssistContext<'_>,
939 parent: &SyntaxNode,
940 ) -> impl Iterator<Item = OutlivedLocal> + 'a {
941 let parent = parent.clone();
942 let range = self.text_range();
943 locals_defined_in_body(&ctx.sema, self)
944 .into_iter()
945 .filter_map(move |local| local_outlives_body(ctx, range, local, &parent))
946 }
947
948 fn external_control_flow<'db>(
950 &self,
951 ctx: &AssistContext<'db>,
952 container_info: &ContainerInfo<'db>,
953 ) -> Option<ControlFlow<'db>> {
954 let mut ret_expr = None;
955 let mut try_expr = None;
956 let mut break_expr = None;
957 let mut continue_expr = None;
958 let mut is_async = false;
959 let mut _is_unsafe = false;
960
961 let mut unsafe_depth = 0;
962 let mut loop_depth = 0;
963
964 self.preorder_expr(&mut |expr| {
965 let expr = match expr {
966 WalkEvent::Enter(e) => e,
967 WalkEvent::Leave(expr) => {
968 match expr {
969 ast::Expr::LoopExpr(_)
970 | ast::Expr::ForExpr(_)
971 | ast::Expr::WhileExpr(_) => loop_depth -= 1,
972 ast::Expr::BlockExpr(block_expr) if block_expr.unsafe_token().is_some() => {
973 unsafe_depth -= 1
974 }
975 _ => (),
976 }
977 return false;
978 }
979 };
980 match expr {
981 ast::Expr::LoopExpr(_) | ast::Expr::ForExpr(_) | ast::Expr::WhileExpr(_) => {
982 loop_depth += 1;
983 }
984 ast::Expr::BlockExpr(block_expr) if block_expr.unsafe_token().is_some() => {
985 unsafe_depth += 1
986 }
987 ast::Expr::ReturnExpr(it) => {
988 ret_expr = Some(it);
989 }
990 ast::Expr::TryExpr(it) => {
991 try_expr = Some(it);
992 }
993 ast::Expr::BreakExpr(it) if loop_depth == 0 => {
994 break_expr = Some(it);
995 }
996 ast::Expr::ContinueExpr(it) if loop_depth == 0 => {
997 continue_expr = Some(it);
998 }
999 ast::Expr::AwaitExpr(_) => is_async = true,
1000 _ => {}
1004 }
1005 false
1006 });
1007
1008 let kind = match (try_expr, ret_expr, break_expr, continue_expr) {
1009 (Some(_), _, None, None) => {
1010 let ret_ty = container_info.ret_type.clone()?;
1011 let kind = TryKind::of_ty(ret_ty, ctx, container_info.edition)?;
1012
1013 Some(FlowKind::Try { kind })
1014 }
1015 (Some(_), _, _, _) => {
1016 cov_mark::hit!(external_control_flow_try_and_bc);
1017 return None;
1018 }
1019 (None, Some(r), None, None) => Some(FlowKind::Return(r.expr())),
1020 (None, Some(_), _, _) => {
1021 cov_mark::hit!(external_control_flow_return_and_bc);
1022 return None;
1023 }
1024 (None, None, Some(_), Some(_)) => {
1025 cov_mark::hit!(external_control_flow_break_and_continue);
1026 return None;
1027 }
1028 (None, None, Some(b), None) => Some(FlowKind::Break(b.lifetime(), b.expr())),
1029 (None, None, None, Some(c)) => Some(FlowKind::Continue(c.lifetime())),
1030 (None, None, None, None) => None,
1031 };
1032
1033 Some(ControlFlow { kind, is_async, is_unsafe: _is_unsafe })
1034 }
1035
1036 fn extracted_function_params<'db>(
1040 &self,
1041 ctx: &AssistContext<'db>,
1042 container_info: &ContainerInfo<'db>,
1043 locals: FxIndexSet<Local>,
1044 ) -> Vec<Param<'db>> {
1045 locals
1046 .into_iter()
1047 .sorted()
1048 .map(|local| (local, local.primary_source(ctx.db())))
1049 .filter(|(_, src)| is_defined_outside_of_body(ctx, self, src))
1050 .filter_map(|(local, src)| match src.into_ident_pat() {
1051 Some(src) => Some((local, src)),
1052 None => {
1053 stdx::never!(false, "Local::is_self returned false, but source is SelfParam");
1054 None
1055 }
1056 })
1057 .map(|(var, src)| {
1058 let usages = LocalUsages::find_local_usages(ctx, var);
1059 let ty = var.ty(ctx.db());
1060
1061 let defined_outside_parent_loop = container_info
1062 .parent_loop
1063 .as_ref()
1064 .is_none_or(|it| it.text_range().contains_range(src.syntax().text_range()));
1065
1066 let is_copy = ty.is_copy(ctx.db());
1067 let has_usages = self.has_usages_after_body(&usages);
1068 let requires_mut =
1069 !ty.is_mutable_reference() && has_exclusive_usages(ctx, &usages, self);
1070 let move_local = (!has_usages && defined_outside_parent_loop) || ty.is_reference();
1074 Param { var, ty, move_local, requires_mut, is_copy }
1075 })
1076 .collect()
1077 }
1078
1079 fn has_usages_after_body(&self, usages: &LocalUsages) -> bool {
1080 usages.iter().any(|reference| self.precedes_range(reference.range))
1081 }
1082}
1083
1084enum GenericParent {
1085 Fn(ast::Fn),
1086 Impl(ast::Impl),
1087 Trait(ast::Trait),
1088}
1089
1090impl GenericParent {
1091 fn generic_param_list(&self) -> Option<ast::GenericParamList> {
1092 match self {
1093 GenericParent::Fn(fn_) => fn_.generic_param_list(),
1094 GenericParent::Impl(impl_) => impl_.generic_param_list(),
1095 GenericParent::Trait(trait_) => trait_.generic_param_list(),
1096 }
1097 }
1098
1099 fn where_clause(&self) -> Option<ast::WhereClause> {
1100 match self {
1101 GenericParent::Fn(fn_) => fn_.where_clause(),
1102 GenericParent::Impl(impl_) => impl_.where_clause(),
1103 GenericParent::Trait(trait_) => trait_.where_clause(),
1104 }
1105 }
1106}
1107
1108fn generic_parents(parent: &SyntaxNode) -> Vec<GenericParent> {
1110 let mut list = Vec::new();
1111 if let Some(parent_item) = parent.ancestors().find_map(ast::Item::cast)
1112 && let ast::Item::Fn(ref fn_) = parent_item
1113 {
1114 if let Some(parent_parent) =
1115 parent_item.syntax().parent().and_then(|it| it.parent()).and_then(ast::Item::cast)
1116 {
1117 match parent_parent {
1118 ast::Item::Impl(impl_) => list.push(GenericParent::Impl(impl_)),
1119 ast::Item::Trait(trait_) => list.push(GenericParent::Trait(trait_)),
1120 _ => (),
1121 }
1122 }
1123 list.push(GenericParent::Fn(fn_.clone()));
1124 }
1125 list
1126}
1127
1128fn has_exclusive_usages(
1130 ctx: &AssistContext<'_>,
1131 usages: &LocalUsages,
1132 body: &FunctionBody,
1133) -> bool {
1134 usages
1135 .iter()
1136 .filter(|reference| body.contains_range(reference.range))
1137 .any(|reference| reference_is_exclusive(reference, body, ctx))
1138}
1139
1140fn reference_is_exclusive(
1142 reference: &FileReference,
1143 node: &dyn HasTokenAtOffset,
1144 ctx: &AssistContext<'_>,
1145) -> bool {
1146 if reference.category.contains(ReferenceCategory::WRITE) {
1154 return true;
1155 }
1156
1157 let path = match path_element_of_reference(node, reference) {
1159 Some(path) => path,
1160 None => return false,
1161 };
1162
1163 expr_require_exclusive_access(ctx, &path).unwrap_or(false)
1164}
1165
1166fn expr_require_exclusive_access(ctx: &AssistContext<'_>, expr: &ast::Expr) -> Option<bool> {
1168 if let ast::Expr::MacroExpr(_) = expr {
1169 return None;
1171 }
1172
1173 let parent = expr.syntax().parent()?;
1174
1175 if let Some(bin_expr) = ast::BinExpr::cast(parent.clone()) {
1176 if matches!(bin_expr.op_kind()?, ast::BinaryOp::Assignment { .. }) {
1177 return Some(bin_expr.lhs()?.syntax() == expr.syntax());
1178 }
1179 return Some(false);
1180 }
1181
1182 if let Some(ref_expr) = ast::RefExpr::cast(parent.clone()) {
1183 return Some(ref_expr.mut_token().is_some());
1184 }
1185
1186 if let Some(method_call) = ast::MethodCallExpr::cast(parent.clone()) {
1187 let func = ctx.sema.resolve_method_call(&method_call)?;
1188 let self_param = func.self_param(ctx.db())?;
1189 let access = self_param.access(ctx.db());
1190
1191 return Some(matches!(access, hir::Access::Exclusive));
1192 }
1193
1194 if let Some(field) = ast::FieldExpr::cast(parent) {
1195 return expr_require_exclusive_access(ctx, &field.into());
1196 }
1197
1198 Some(false)
1199}
1200
1201trait HasTokenAtOffset {
1202 fn token_at_offset(&self, offset: TextSize) -> TokenAtOffset<SyntaxToken>;
1203}
1204
1205impl HasTokenAtOffset for SyntaxNode {
1206 fn token_at_offset(&self, offset: TextSize) -> TokenAtOffset<SyntaxToken> {
1207 SyntaxNode::token_at_offset(self, offset)
1208 }
1209}
1210
1211impl HasTokenAtOffset for FunctionBody {
1212 fn token_at_offset(&self, offset: TextSize) -> TokenAtOffset<SyntaxToken> {
1213 match self {
1214 FunctionBody::Expr(expr) => expr.syntax().token_at_offset(offset),
1215 FunctionBody::Span { parent, text_range, .. } => {
1216 match parent.syntax().token_at_offset(offset) {
1217 TokenAtOffset::None => TokenAtOffset::None,
1218 TokenAtOffset::Single(t) => {
1219 if text_range.contains_range(t.text_range()) {
1220 TokenAtOffset::Single(t)
1221 } else {
1222 TokenAtOffset::None
1223 }
1224 }
1225 TokenAtOffset::Between(a, b) => {
1226 match (
1227 text_range.contains_range(a.text_range()),
1228 text_range.contains_range(b.text_range()),
1229 ) {
1230 (true, true) => TokenAtOffset::Between(a, b),
1231 (true, false) => TokenAtOffset::Single(a),
1232 (false, true) => TokenAtOffset::Single(b),
1233 (false, false) => TokenAtOffset::None,
1234 }
1235 }
1236 }
1237 }
1238 }
1239 }
1240}
1241
1242fn path_element_of_reference(
1248 node: &dyn HasTokenAtOffset,
1249 reference: &FileReference,
1250) -> Option<ast::Expr> {
1251 let token = node.token_at_offset(reference.range.start()).right_biased().or_else(|| {
1252 stdx::never!(false, "cannot find token at variable usage: {:?}", reference);
1253 None
1254 })?;
1255 let path = token.parent_ancestors().find_map(ast::Expr::cast).or_else(|| {
1256 stdx::never!(false, "cannot find path parent of variable usage: {:?}", token);
1257 None
1258 })?;
1259 stdx::always!(
1260 matches!(path, ast::Expr::PathExpr(_) | ast::Expr::MacroExpr(_)),
1261 "unexpected expression type for variable usage: {:?}",
1262 path
1263 );
1264 Some(path)
1265}
1266
1267fn locals_defined_in_body(
1269 sema: &Semantics<'_, RootDatabase>,
1270 body: &FunctionBody,
1271) -> FxIndexSet<Local> {
1272 let mut res = FxIndexSet::default();
1275 body.walk_pat(&mut |pat| {
1276 if let ast::Pat::IdentPat(pat) = pat
1277 && let Some(local) = sema.to_def(&pat)
1278 {
1279 res.insert(local);
1280 }
1281 });
1282 res
1283}
1284
1285fn local_outlives_body(
1287 ctx: &AssistContext<'_>,
1288 body_range: TextRange,
1289 local: Local,
1290 parent: &SyntaxNode,
1291) -> Option<OutlivedLocal> {
1292 let usages = LocalUsages::find_local_usages(ctx, local);
1293 let mut has_mut_usages = false;
1294 let mut any_outlives = false;
1295 for usage in usages.iter() {
1296 if body_range.end() <= usage.range.start() {
1297 has_mut_usages |= reference_is_exclusive(usage, parent, ctx);
1298 any_outlives |= true;
1299 if has_mut_usages {
1300 break; }
1302 }
1303 }
1304 if !any_outlives {
1305 return None;
1306 }
1307 Some(OutlivedLocal { local, mut_usage_outside_body: has_mut_usages })
1308}
1309
1310fn is_defined_outside_of_body(
1312 ctx: &AssistContext<'_>,
1313 body: &FunctionBody,
1314 src: &LocalSource,
1315) -> bool {
1316 src.original_file(ctx.db()) == ctx.file_id() && !body.contains_node(src.syntax())
1317}
1318
1319fn node_to_insert_after(body: &FunctionBody, anchor: Anchor) -> Option<SyntaxNode> {
1323 let node = body.node();
1324 let mut ancestors = node.ancestors().peekable();
1325 let mut last_ancestor = None;
1326 while let Some(next_ancestor) = ancestors.next() {
1327 match next_ancestor.kind() {
1328 SyntaxKind::SOURCE_FILE => break,
1329 SyntaxKind::IMPL => {
1330 if body.extracted_from_trait_impl() && matches!(anchor, Anchor::Method) {
1331 let impl_node = find_non_trait_impl(&next_ancestor);
1332 if let target_node @ Some(_) = impl_node.as_ref().and_then(last_impl_member) {
1333 return target_node;
1334 }
1335 }
1336 }
1337 SyntaxKind::ITEM_LIST if !matches!(anchor, Anchor::Freestanding) => continue,
1338 SyntaxKind::ITEM_LIST => {
1339 if ancestors.peek().map(SyntaxNode::kind) == Some(SyntaxKind::MODULE) {
1340 break;
1341 }
1342 }
1343 SyntaxKind::ASSOC_ITEM_LIST if !matches!(anchor, Anchor::Method) => continue,
1344 SyntaxKind::ASSOC_ITEM_LIST if body.extracted_from_trait_impl() => continue,
1345 SyntaxKind::ASSOC_ITEM_LIST => {
1346 if ancestors.peek().map(SyntaxNode::kind) == Some(SyntaxKind::IMPL) {
1347 break;
1348 }
1349 }
1350 _ => (),
1351 }
1352 last_ancestor = Some(next_ancestor);
1353 }
1354 last_ancestor
1355}
1356
1357fn find_non_trait_impl(trait_impl: &SyntaxNode) -> Option<ast::Impl> {
1358 let as_impl = ast::Impl::cast(trait_impl.clone())?;
1359 let impl_type = Some(impl_type_name(&as_impl)?);
1360
1361 let siblings = trait_impl.parent()?.children();
1362 siblings
1363 .filter_map(ast::Impl::cast)
1364 .find(|s| impl_type_name(s) == impl_type && !is_trait_impl(s))
1365}
1366
1367fn last_impl_member(impl_node: &ast::Impl) -> Option<SyntaxNode> {
1368 let last_child = impl_node.assoc_item_list()?.assoc_items().last()?;
1369 Some(last_child.syntax().clone())
1370}
1371
1372fn is_trait_impl(node: &ast::Impl) -> bool {
1373 node.trait_().is_some()
1374}
1375
1376fn impl_type_name(impl_node: &ast::Impl) -> Option<String> {
1377 Some(impl_node.self_ty()?.to_string())
1378}
1379
1380fn fixup_call_site(builder: &mut SourceChangeBuilder, body: &FunctionBody) {
1382 let parent_match_arm = body.parent().and_then(ast::MatchArm::cast);
1383
1384 if let Some(parent_match_arm) = parent_match_arm
1385 && parent_match_arm.comma_token().is_none()
1386 {
1387 let parent_match_arm = builder.make_mut(parent_match_arm);
1388 ted::append_child_raw(parent_match_arm.syntax(), make::token(T![,]));
1389 }
1390}
1391
1392fn make_call(ctx: &AssistContext<'_>, fun: &Function<'_>, indent: IndentLevel) -> SyntaxNode {
1393 let ret_ty = fun.return_type(ctx);
1394
1395 let args = make::arg_list(fun.params.iter().map(|param| param.to_arg(ctx, fun.mods.edition)));
1396 let name = fun.name.clone();
1397 let mut call_expr = if fun.self_param.is_some() {
1398 let self_arg = make::expr_path(make::ext::ident_path("self"));
1399 make::expr_method_call(self_arg, name, args).into()
1400 } else {
1401 let func = make::expr_path(make::path_unqualified(make::path_segment(name)));
1402 make::expr_call(func, args).into()
1403 };
1404
1405 let handler = FlowHandler::from_ret_ty(fun, &ret_ty);
1406
1407 if fun.control_flow.is_async {
1408 call_expr = make::expr_await(call_expr);
1409 }
1410
1411 let expr = handler.make_call_expr(call_expr).clone_for_update();
1412 expr.indent(indent);
1413
1414 let outliving_bindings = match fun.outliving_locals.as_slice() {
1415 [] => None,
1416 [var] => {
1417 let name = var.local.name(ctx.db());
1418 let name = make::name(&name.display(ctx.db(), fun.mods.edition).to_string());
1419 Some(ast::Pat::IdentPat(make::ident_pat(false, var.mut_usage_outside_body, name)))
1420 }
1421 vars => {
1422 let binding_pats = vars.iter().map(|var| {
1423 let name = var.local.name(ctx.db());
1424 let name = make::name(&name.display(ctx.db(), fun.mods.edition).to_string());
1425 make::ident_pat(false, var.mut_usage_outside_body, name).into()
1426 });
1427 Some(ast::Pat::TuplePat(make::tuple_pat(binding_pats)))
1428 }
1429 };
1430
1431 let parent_match_arm = fun.body.parent().and_then(ast::MatchArm::cast);
1432
1433 if let Some(bindings) = outliving_bindings {
1434 make::let_stmt(bindings, None, Some(expr)).syntax().clone_for_update()
1436 } else if parent_match_arm.as_ref().is_some() {
1437 expr.syntax().clone()
1439 } else if parent_match_arm.as_ref().is_none()
1440 && fun.ret_ty.is_unit()
1441 && (!fun.outliving_locals.is_empty() || !expr.is_block_like())
1442 {
1443 make::expr_stmt(expr).syntax().clone_for_update()
1445 } else {
1446 expr.syntax().clone()
1448 }
1449}
1450
1451enum FlowHandler<'db> {
1452 None,
1453 Try { kind: TryKind<'db> },
1454 If { action: FlowKind<'db> },
1455 IfOption { action: FlowKind<'db> },
1456 MatchOption { none: FlowKind<'db> },
1457 MatchResult { err: FlowKind<'db> },
1458}
1459
1460impl<'db> FlowHandler<'db> {
1461 fn from_ret_ty(fun: &Function<'db>, ret_ty: &FunType<'db>) -> FlowHandler<'db> {
1462 if fun.contains_tail_expr {
1463 return FlowHandler::None;
1464 }
1465 let Some(action) = fun.control_flow.kind.clone() else {
1466 return FlowHandler::None;
1467 };
1468
1469 if let FunType::Unit = ret_ty {
1470 match action {
1471 FlowKind::Return(None) | FlowKind::Break(_, None) | FlowKind::Continue(_) => {
1472 FlowHandler::If { action }
1473 }
1474 FlowKind::Return(_) | FlowKind::Break(_, _) => FlowHandler::IfOption { action },
1475 FlowKind::Try { kind } => FlowHandler::Try { kind },
1476 }
1477 } else {
1478 match action {
1479 FlowKind::Return(None) | FlowKind::Break(_, None) | FlowKind::Continue(_) => {
1480 FlowHandler::MatchOption { none: action }
1481 }
1482 FlowKind::Return(_) | FlowKind::Break(_, _) => {
1483 FlowHandler::MatchResult { err: action }
1484 }
1485 FlowKind::Try { kind } => FlowHandler::Try { kind },
1486 }
1487 }
1488 }
1489
1490 fn make_call_expr(&self, call_expr: ast::Expr) -> ast::Expr {
1491 match self {
1492 FlowHandler::None => call_expr,
1493 FlowHandler::Try { kind: _ } => make::expr_try(call_expr),
1494 FlowHandler::If { action } => {
1495 let action = action.make_result_handler(None);
1496 let stmt = make::expr_stmt(action);
1497 let block = make::block_expr(iter::once(stmt.into()), None);
1498 let controlflow_break_path = make::path_from_text("ControlFlow::Break");
1499 let condition = make::expr_let(
1500 make::tuple_struct_pat(
1501 controlflow_break_path,
1502 iter::once(make::wildcard_pat().into()),
1503 )
1504 .into(),
1505 call_expr,
1506 );
1507 make::expr_if(condition.into(), block, None).into()
1508 }
1509 FlowHandler::IfOption { action } => {
1510 let path = make::ext::ident_path("Some");
1511 let value_pat = make::ext::simple_ident_pat(make::name("value"));
1512 let pattern = make::tuple_struct_pat(path, iter::once(value_pat.into()));
1513 let cond = make::expr_let(pattern.into(), call_expr);
1514 let value = make::expr_path(make::ext::ident_path("value"));
1515 let action_expr = action.make_result_handler(Some(value));
1516 let action_stmt = make::expr_stmt(action_expr);
1517 let then = make::block_expr(iter::once(action_stmt.into()), None);
1518 make::expr_if(cond.into(), then, None).into()
1519 }
1520 FlowHandler::MatchOption { none } => {
1521 let some_name = "value";
1522
1523 let some_arm = {
1524 let path = make::ext::ident_path("Some");
1525 let value_pat = make::ext::simple_ident_pat(make::name(some_name));
1526 let pat = make::tuple_struct_pat(path, iter::once(value_pat.into()));
1527 let value = make::expr_path(make::ext::ident_path(some_name));
1528 make::match_arm(pat.into(), None, value)
1529 };
1530 let none_arm = {
1531 let path = make::ext::ident_path("None");
1532 let pat = make::path_pat(path);
1533 make::match_arm(pat, None, none.make_result_handler(None))
1534 };
1535 let arms = make::match_arm_list(vec![some_arm, none_arm]);
1536 make::expr_match(call_expr, arms).into()
1537 }
1538 FlowHandler::MatchResult { err } => {
1539 let ok_name = "value";
1540 let err_name = "value";
1541
1542 let ok_arm = {
1543 let path = make::ext::ident_path("Ok");
1544 let value_pat = make::ext::simple_ident_pat(make::name(ok_name));
1545 let pat = make::tuple_struct_pat(path, iter::once(value_pat.into()));
1546 let value = make::expr_path(make::ext::ident_path(ok_name));
1547 make::match_arm(pat.into(), None, value)
1548 };
1549 let err_arm = {
1550 let path = make::ext::ident_path("Err");
1551 let value_pat = make::ext::simple_ident_pat(make::name(err_name));
1552 let pat = make::tuple_struct_pat(path, iter::once(value_pat.into()));
1553 let value = make::expr_path(make::ext::ident_path(err_name));
1554 make::match_arm(pat.into(), None, err.make_result_handler(Some(value)))
1555 };
1556 let arms = make::match_arm_list(vec![ok_arm, err_arm]);
1557 make::expr_match(call_expr, arms).into()
1558 }
1559 }
1560 }
1561}
1562
1563fn path_expr_from_local(ctx: &AssistContext<'_>, var: Local, edition: Edition) -> ast::Expr {
1564 let name = var.name(ctx.db()).display(ctx.db(), edition).to_string();
1565 make::expr_path(make::ext::ident_path(&name))
1566}
1567
1568fn format_function(
1569 ctx: &AssistContext<'_>,
1570 module: hir::Module,
1571 fun: &Function<'_>,
1572 old_indent: IndentLevel,
1573) -> ast::Fn {
1574 let fun_name = make::name(&fun.name.text());
1575 let params = fun.make_param_list(ctx, module, fun.mods.edition);
1576 let ret_ty = fun.make_ret_ty(ctx, module);
1577 let body = make_body(ctx, old_indent, fun);
1578 let (generic_params, where_clause) = make_generic_params_and_where_clause(ctx, fun);
1579
1580 make::fn_(
1581 None,
1582 None,
1583 fun_name,
1584 generic_params,
1585 where_clause,
1586 params,
1587 body,
1588 ret_ty,
1589 fun.control_flow.is_async,
1590 fun.mods.is_const,
1591 fun.control_flow.is_unsafe,
1592 false,
1593 )
1594}
1595
1596fn make_generic_params_and_where_clause(
1597 ctx: &AssistContext<'_>,
1598 fun: &Function<'_>,
1599) -> (Option<ast::GenericParamList>, Option<ast::WhereClause>) {
1600 let used_type_params = fun.type_params(ctx);
1601
1602 let generic_param_list = make_generic_param_list(ctx, fun, &used_type_params);
1603 let where_clause = make_where_clause(ctx, fun, &used_type_params);
1604
1605 (generic_param_list, where_clause)
1606}
1607
1608fn make_generic_param_list(
1609 ctx: &AssistContext<'_>,
1610 fun: &Function<'_>,
1611 used_type_params: &[TypeParam],
1612) -> Option<ast::GenericParamList> {
1613 let mut generic_params = fun
1614 .mods
1615 .generic_param_lists
1616 .iter()
1617 .flat_map(|parent_params| {
1618 parent_params
1619 .generic_params()
1620 .filter(|param| param_is_required(ctx, param, used_type_params))
1621 })
1622 .peekable();
1623
1624 if generic_params.peek().is_some() {
1625 Some(make::generic_param_list(generic_params))
1626 } else {
1627 None
1628 }
1629}
1630
1631fn param_is_required(
1632 ctx: &AssistContext<'_>,
1633 param: &ast::GenericParam,
1634 used_type_params: &[TypeParam],
1635) -> bool {
1636 match param {
1637 ast::GenericParam::ConstParam(_) | ast::GenericParam::LifetimeParam(_) => false,
1638 ast::GenericParam::TypeParam(type_param) => match &ctx.sema.to_def(type_param) {
1639 Some(def) => used_type_params.contains(def),
1640 _ => false,
1641 },
1642 }
1643}
1644
1645fn make_where_clause(
1646 ctx: &AssistContext<'_>,
1647 fun: &Function<'_>,
1648 used_type_params: &[TypeParam],
1649) -> Option<ast::WhereClause> {
1650 let mut predicates = fun
1651 .mods
1652 .where_clauses
1653 .iter()
1654 .flat_map(|parent_where_clause| {
1655 parent_where_clause
1656 .predicates()
1657 .filter(|pred| pred_is_required(ctx, pred, used_type_params))
1658 })
1659 .peekable();
1660
1661 if predicates.peek().is_some() { Some(make::where_clause(predicates)) } else { None }
1662}
1663
1664fn pred_is_required(
1665 ctx: &AssistContext<'_>,
1666 pred: &ast::WherePred,
1667 used_type_params: &[TypeParam],
1668) -> bool {
1669 match resolved_type_param(ctx, pred) {
1670 Some(it) => used_type_params.contains(&it),
1671 None => false,
1672 }
1673}
1674
1675fn resolved_type_param(ctx: &AssistContext<'_>, pred: &ast::WherePred) -> Option<TypeParam> {
1676 let path = match pred.ty()? {
1677 ast::Type::PathType(path_type) => path_type.path(),
1678 _ => None,
1679 }?;
1680
1681 match ctx.sema.resolve_path(&path)? {
1682 PathResolution::TypeParam(type_param) => Some(type_param),
1683 _ => None,
1684 }
1685}
1686
1687impl<'db> Function<'db> {
1688 fn type_params(&self, ctx: &AssistContext<'db>) -> Vec<TypeParam> {
1690 let type_params_in_descendant_paths =
1691 self.body.descendant_paths().filter_map(|it| match ctx.sema.resolve_path(&it) {
1692 Some(PathResolution::TypeParam(type_param)) => Some(type_param),
1693 _ => None,
1694 });
1695 let type_params_in_params = self.params.iter().filter_map(|p| p.ty.as_type_param(ctx.db()));
1696 type_params_in_descendant_paths.chain(type_params_in_params).collect()
1697 }
1698
1699 fn make_param_list(
1700 &self,
1701 ctx: &AssistContext<'_>,
1702 module: hir::Module,
1703 edition: Edition,
1704 ) -> ast::ParamList {
1705 let self_param = self.self_param.clone();
1706 let params = self.params.iter().map(|param| param.to_param(ctx, module, edition));
1707 make::param_list(self_param, params)
1708 }
1709
1710 fn make_ret_ty(&self, ctx: &AssistContext<'_>, module: hir::Module) -> Option<ast::RetType> {
1711 let fun_ty = self.return_type(ctx);
1712 let handler = FlowHandler::from_ret_ty(self, &fun_ty);
1713 let ret_ty = match &handler {
1714 FlowHandler::None => {
1715 if matches!(fun_ty, FunType::Unit) {
1716 return None;
1717 }
1718 fun_ty.make_ty(ctx, module)
1719 }
1720 FlowHandler::Try { kind: TryKind::Option } => {
1721 make::ext::ty_option(fun_ty.make_ty(ctx, module))
1722 }
1723 FlowHandler::Try { kind: TryKind::Result { ty: parent_ret_ty } } => {
1724 let handler_ty = parent_ret_ty
1725 .type_arguments()
1726 .nth(1)
1727 .map(|ty| make_ty(&ty, ctx, module))
1728 .unwrap_or_else(make::ty_placeholder);
1729 make::ext::ty_result(fun_ty.make_ty(ctx, module), handler_ty)
1730 }
1731 FlowHandler::If { .. } => make::ty("ControlFlow<()>"),
1732 FlowHandler::IfOption { action } => {
1733 let handler_ty = action
1734 .expr_ty(ctx)
1735 .map(|ty| make_ty(&ty, ctx, module))
1736 .unwrap_or_else(make::ty_placeholder);
1737 make::ext::ty_option(handler_ty)
1738 }
1739 FlowHandler::MatchOption { .. } => make::ext::ty_option(fun_ty.make_ty(ctx, module)),
1740 FlowHandler::MatchResult { err } => {
1741 let handler_ty = err
1742 .expr_ty(ctx)
1743 .map(|ty| make_ty(&ty, ctx, module))
1744 .unwrap_or_else(make::ty_placeholder);
1745 make::ext::ty_result(fun_ty.make_ty(ctx, module), handler_ty)
1746 }
1747 };
1748 Some(make::ret_type(ret_ty))
1749 }
1750}
1751
1752impl<'db> FunType<'db> {
1753 fn make_ty(&self, ctx: &AssistContext<'db>, module: hir::Module) -> ast::Type {
1754 match self {
1755 FunType::Unit => make::ty_unit(),
1756 FunType::Single(ty) => make_ty(ty, ctx, module),
1757 FunType::Tuple(types) => match types.as_slice() {
1758 [] => {
1759 stdx::never!("tuple type with 0 elements");
1760 make::ty_unit()
1761 }
1762 [ty] => {
1763 stdx::never!("tuple type with 1 element");
1764 make_ty(ty, ctx, module)
1765 }
1766 types => {
1767 let types = types.iter().map(|ty| make_ty(ty, ctx, module));
1768 make::ty_tuple(types)
1769 }
1770 },
1771 }
1772 }
1773}
1774
1775fn make_body(
1776 ctx: &AssistContext<'_>,
1777 old_indent: IndentLevel,
1778 fun: &Function<'_>,
1779) -> ast::BlockExpr {
1780 let ret_ty = fun.return_type(ctx);
1781 let handler = FlowHandler::from_ret_ty(fun, &ret_ty);
1782
1783 let block = match &fun.body {
1784 FunctionBody::Expr(expr) => {
1785 let expr = rewrite_body_segment(ctx, &fun.params, &handler, expr.syntax());
1786 let expr = ast::Expr::cast(expr).expect("Body segment should be an expr");
1787 match expr {
1788 ast::Expr::BlockExpr(block) => {
1789 block.dedent(old_indent);
1791 let elements = block.stmt_list().map_or_else(
1792 || Either::Left(iter::empty()),
1793 |stmt_list| {
1794 let elements = stmt_list.syntax().children_with_tokens().filter_map(
1795 |node_or_token| match &node_or_token {
1796 syntax::NodeOrToken::Node(node) => {
1797 ast::Stmt::cast(node.clone()).map(|_| node_or_token)
1798 }
1799 syntax::NodeOrToken::Token(token) => {
1800 ast::Comment::cast(token.clone()).map(|_| node_or_token)
1801 }
1802 },
1803 );
1804 Either::Right(elements)
1805 },
1806 );
1807 make::hacky_block_expr(elements, block.tail_expr())
1808 }
1809 _ => {
1810 expr.reindent_to(1.into());
1811
1812 make::block_expr(Vec::new(), Some(expr))
1813 }
1814 }
1815 }
1816 FunctionBody::Span { parent, text_range, .. } => {
1817 let mut elements: Vec<_> = parent
1818 .syntax()
1819 .children_with_tokens()
1820 .filter(|it| text_range.contains_range(it.text_range()))
1821 .map(|it| match &it {
1822 syntax::NodeOrToken::Node(n) => syntax::NodeOrToken::Node(
1823 rewrite_body_segment(ctx, &fun.params, &handler, n),
1824 ),
1825 _ => it,
1826 })
1827 .collect();
1828
1829 let mut tail_expr = match &elements.last() {
1830 Some(syntax::NodeOrToken::Node(node)) if ast::Expr::can_cast(node.kind()) => {
1831 ast::Expr::cast(node.clone())
1832 }
1833 _ => None,
1834 };
1835
1836 match tail_expr {
1837 Some(_) => {
1838 elements.pop();
1839 }
1840 None => match fun.outliving_locals.as_slice() {
1841 [] => {}
1842 [var] => {
1843 tail_expr = Some(path_expr_from_local(ctx, var.local, fun.mods.edition));
1844 }
1845 vars => {
1846 let exprs = vars
1847 .iter()
1848 .map(|var| path_expr_from_local(ctx, var.local, fun.mods.edition));
1849 let expr = make::expr_tuple(exprs);
1850 tail_expr = Some(expr.into());
1851 }
1852 },
1853 };
1854
1855 let body_indent = IndentLevel(1);
1856 let elements = elements
1857 .into_iter()
1858 .map(|node_or_token| match &node_or_token {
1859 syntax::NodeOrToken::Node(node) => match ast::Stmt::cast(node.clone()) {
1860 Some(stmt) => {
1861 stmt.reindent_to(body_indent);
1862 let ast_node = stmt.syntax().clone_subtree();
1863 syntax::NodeOrToken::Node(ast_node)
1864 }
1865 _ => node_or_token,
1866 },
1867 _ => node_or_token,
1868 })
1869 .collect::<Vec<SyntaxElement>>();
1870 if let Some(tail_expr) = &mut tail_expr {
1871 tail_expr.reindent_to(body_indent);
1872 }
1873
1874 make::hacky_block_expr(elements, tail_expr)
1875 }
1876 };
1877
1878 match &handler {
1879 FlowHandler::None => block,
1880 FlowHandler::Try { kind } => {
1881 let block = with_default_tail_expr(block, make::ext::expr_unit());
1882 map_tail_expr(block, |tail_expr| {
1883 let constructor = match kind {
1884 TryKind::Option => "Some",
1885 TryKind::Result { .. } => "Ok",
1886 };
1887 let func = make::expr_path(make::ext::ident_path(constructor));
1888 let args = make::arg_list(iter::once(tail_expr));
1889 make::expr_call(func, args).into()
1890 })
1891 }
1892 FlowHandler::If { .. } => {
1893 let controlflow_continue = make::expr_call(
1894 make::expr_path(make::path_from_text("ControlFlow::Continue")),
1895 make::arg_list([make::ext::expr_unit()]),
1896 )
1897 .into();
1898 with_tail_expr(block, controlflow_continue)
1899 }
1900 FlowHandler::IfOption { .. } => {
1901 let none = make::expr_path(make::ext::ident_path("None"));
1902 with_tail_expr(block, none)
1903 }
1904 FlowHandler::MatchOption { .. } => map_tail_expr(block, |tail_expr| {
1905 let some = make::expr_path(make::ext::ident_path("Some"));
1906 let args = make::arg_list(iter::once(tail_expr));
1907 make::expr_call(some, args).into()
1908 }),
1909 FlowHandler::MatchResult { .. } => map_tail_expr(block, |tail_expr| {
1910 let ok = make::expr_path(make::ext::ident_path("Ok"));
1911 let args = make::arg_list(iter::once(tail_expr));
1912 make::expr_call(ok, args).into()
1913 }),
1914 }
1915}
1916
1917fn map_tail_expr(block: ast::BlockExpr, f: impl FnOnce(ast::Expr) -> ast::Expr) -> ast::BlockExpr {
1918 let tail_expr = match block.tail_expr() {
1919 Some(tail_expr) => tail_expr,
1920 None => return block,
1921 };
1922 make::block_expr(block.statements(), Some(f(tail_expr)))
1923}
1924
1925fn with_default_tail_expr(block: ast::BlockExpr, tail_expr: ast::Expr) -> ast::BlockExpr {
1926 match block.tail_expr() {
1927 Some(_) => block,
1928 None => make::block_expr(block.statements(), Some(tail_expr)),
1929 }
1930}
1931
1932fn with_tail_expr(block: ast::BlockExpr, tail_expr: ast::Expr) -> ast::BlockExpr {
1933 let stmt_tail_opt: Option<ast::Stmt> =
1934 block.tail_expr().map(|expr| make::expr_stmt(expr).into());
1935
1936 let mut elements: Vec<SyntaxElement> = vec![];
1937
1938 block.statements().for_each(|stmt| {
1939 elements.push(syntax::NodeOrToken::Node(stmt.syntax().clone()));
1940 });
1941
1942 if let Some(stmt_list) = block.stmt_list() {
1943 stmt_list.syntax().children_with_tokens().for_each(|node_or_token| {
1944 if let syntax::NodeOrToken::Token(_) = &node_or_token {
1945 elements.push(node_or_token)
1946 };
1947 });
1948 }
1949
1950 if let Some(stmt_tail) = stmt_tail_opt {
1951 elements.push(syntax::NodeOrToken::Node(stmt_tail.syntax().clone()));
1952 }
1953
1954 make::hacky_block_expr(elements, Some(tail_expr))
1955}
1956
1957fn format_type(ty: &hir::Type<'_>, ctx: &AssistContext<'_>, module: hir::Module) -> String {
1958 ty.display_source_code(ctx.db(), module.into(), true).ok().unwrap_or_else(|| "_".to_owned())
1959}
1960
1961fn make_ty(ty: &hir::Type<'_>, ctx: &AssistContext<'_>, module: hir::Module) -> ast::Type {
1962 let ty_str = format_type(ty, ctx, module);
1963 make::ty(&ty_str)
1964}
1965
1966fn rewrite_body_segment(
1967 ctx: &AssistContext<'_>,
1968 params: &[Param<'_>],
1969 handler: &FlowHandler<'_>,
1970 syntax: &SyntaxNode,
1971) -> SyntaxNode {
1972 let syntax = fix_param_usages(ctx, params, syntax);
1973 update_external_control_flow(handler, &syntax);
1974 syntax
1975}
1976
1977fn fix_param_usages(
1979 ctx: &AssistContext<'_>,
1980 params: &[Param<'_>],
1981 syntax: &SyntaxNode,
1982) -> SyntaxNode {
1983 let mut usages_for_param: Vec<(&Param<'_>, Vec<ast::Expr>)> = Vec::new();
1984
1985 let tm = TreeMutator::new(syntax);
1986
1987 for param in params {
1988 if !param.kind().is_ref() {
1989 continue;
1990 }
1991
1992 let usages = LocalUsages::find_local_usages(ctx, param.var);
1993 let usages = usages
1994 .iter()
1995 .filter(|reference| syntax.text_range().contains_range(reference.range))
1996 .filter_map(|reference| path_element_of_reference(syntax, reference))
1997 .map(|expr| tm.make_mut(&expr));
1998
1999 usages_for_param.push((param, usages.unique().collect()));
2000 }
2001
2002 let res = tm.make_syntax_mut(syntax);
2003
2004 for (param, usages) in usages_for_param {
2005 for usage in usages {
2006 match usage.syntax().ancestors().skip(1).find_map(ast::Expr::cast) {
2007 Some(ast::Expr::MethodCallExpr(_) | ast::Expr::FieldExpr(_)) => {
2008 }
2010 Some(ast::Expr::RefExpr(node))
2011 if param.kind() == ParamKind::MutRef && node.mut_token().is_some() =>
2012 {
2013 ted::replace(
2014 node.syntax(),
2015 node.expr().expect("RefExpr::expr() cannot be None").syntax(),
2016 );
2017 }
2018 Some(ast::Expr::RefExpr(node))
2019 if param.kind() == ParamKind::SharedRef && node.mut_token().is_none() =>
2020 {
2021 ted::replace(
2022 node.syntax(),
2023 node.expr().expect("RefExpr::expr() cannot be None").syntax(),
2024 );
2025 }
2026 Some(_) | None => {
2027 let p = &make::expr_prefix(T![*], usage.clone()).clone_for_update();
2028 ted::replace(usage.syntax(), p.syntax())
2029 }
2030 }
2031 }
2032 }
2033
2034 res
2035}
2036
2037fn update_external_control_flow(handler: &FlowHandler<'_>, syntax: &SyntaxNode) {
2038 let mut nested_loop = None;
2039 let mut nested_scope = None;
2040 for event in syntax.preorder() {
2041 match event {
2042 WalkEvent::Enter(e) => match e.kind() {
2043 SyntaxKind::LOOP_EXPR | SyntaxKind::WHILE_EXPR | SyntaxKind::FOR_EXPR => {
2044 if nested_loop.is_none() {
2045 nested_loop = Some(e.clone());
2046 }
2047 }
2048 SyntaxKind::FN
2049 | SyntaxKind::CONST
2050 | SyntaxKind::STATIC
2051 | SyntaxKind::IMPL
2052 | SyntaxKind::MODULE => {
2053 if nested_scope.is_none() {
2054 nested_scope = Some(e.clone());
2055 }
2056 }
2057 _ => {}
2058 },
2059 WalkEvent::Leave(e) => {
2060 if nested_scope.is_none()
2061 && let Some(expr) = ast::Expr::cast(e.clone())
2062 {
2063 match expr {
2064 ast::Expr::ReturnExpr(return_expr) => {
2065 let expr = return_expr.expr();
2066 if let Some(replacement) = make_rewritten_flow(handler, expr) {
2067 ted::replace(return_expr.syntax(), replacement.syntax())
2068 }
2069 }
2070 ast::Expr::BreakExpr(break_expr) if nested_loop.is_none() => {
2071 let expr = break_expr.expr();
2072 if let Some(replacement) = make_rewritten_flow(handler, expr) {
2073 ted::replace(break_expr.syntax(), replacement.syntax())
2074 }
2075 }
2076 ast::Expr::ContinueExpr(continue_expr) if nested_loop.is_none() => {
2077 if let Some(replacement) = make_rewritten_flow(handler, None) {
2078 ted::replace(continue_expr.syntax(), replacement.syntax())
2079 }
2080 }
2081 _ => {
2082 }
2084 }
2085 }
2086
2087 if nested_loop.as_ref() == Some(&e) {
2088 nested_loop = None;
2089 }
2090 if nested_scope.as_ref() == Some(&e) {
2091 nested_scope = None;
2092 }
2093 }
2094 };
2095 }
2096}
2097
2098fn make_rewritten_flow(
2099 handler: &FlowHandler<'_>,
2100 arg_expr: Option<ast::Expr>,
2101) -> Option<ast::Expr> {
2102 let value = match handler {
2103 FlowHandler::None | FlowHandler::Try { .. } => return None,
2104 FlowHandler::If { .. } => make::expr_call(
2105 make::expr_path(make::path_from_text("ControlFlow::Break")),
2106 make::arg_list([make::ext::expr_unit()]),
2107 )
2108 .into(),
2109 FlowHandler::IfOption { .. } => {
2110 let expr = arg_expr.unwrap_or_else(make::ext::expr_unit);
2111 let args = make::arg_list([expr]);
2112 make::expr_call(make::expr_path(make::ext::ident_path("Some")), args).into()
2113 }
2114 FlowHandler::MatchOption { .. } => make::expr_path(make::ext::ident_path("None")),
2115 FlowHandler::MatchResult { .. } => {
2116 let expr = arg_expr.unwrap_or_else(make::ext::expr_unit);
2117 let args = make::arg_list([expr]);
2118 make::expr_call(make::expr_path(make::ext::ident_path("Err")), args).into()
2119 }
2120 };
2121 Some(make::expr_return(Some(value)).clone_for_update())
2122}
2123
2124#[cfg(test)]
2125mod tests {
2126 use crate::tests::{check_assist, check_assist_not_applicable};
2127
2128 use super::*;
2129
2130 #[test]
2131 fn no_args_from_binary_expr() {
2132 check_assist(
2133 extract_function,
2134 r#"
2135fn foo() {
2136 foo($01 + 1$0);
2137}
2138"#,
2139 r#"
2140fn foo() {
2141 foo(fun_name());
2142}
2143
2144fn $0fun_name() -> i32 {
2145 1 + 1
2146}
2147"#,
2148 );
2149 }
2150
2151 #[test]
2152 fn no_args_from_binary_expr_in_module() {
2153 check_assist(
2154 extract_function,
2155 r#"
2156mod bar {
2157 fn foo() {
2158 foo($01 + 1$0);
2159 }
2160}
2161"#,
2162 r#"
2163mod bar {
2164 fn foo() {
2165 foo(fun_name());
2166 }
2167
2168 fn $0fun_name() -> i32 {
2169 1 + 1
2170 }
2171}
2172"#,
2173 );
2174 }
2175
2176 #[test]
2177 fn no_args_from_binary_expr_indented() {
2178 check_assist(
2179 extract_function,
2180 r#"
2181fn foo() {
2182 $0{ 1 + 1 }$0;
2183}
2184"#,
2185 r#"
2186fn foo() {
2187 fun_name();
2188}
2189
2190fn $0fun_name() -> i32 {
2191 1 + 1
2192}
2193"#,
2194 );
2195 }
2196
2197 #[test]
2198 fn no_args_from_stmt_with_last_expr() {
2199 check_assist(
2200 extract_function,
2201 r#"
2202fn foo() -> i32 {
2203 let k = 1;
2204 $0let m = 1;
2205 m + 1$0
2206}
2207"#,
2208 r#"
2209fn foo() -> i32 {
2210 let k = 1;
2211 fun_name()
2212}
2213
2214fn $0fun_name() -> i32 {
2215 let m = 1;
2216 m + 1
2217}
2218"#,
2219 );
2220 }
2221
2222 #[test]
2223 fn no_args_from_stmt_unit() {
2224 check_assist(
2225 extract_function,
2226 r#"
2227fn foo() {
2228 let k = 3;
2229 $0let m = 1;
2230 let n = m + 1;$0
2231 let g = 5;
2232}
2233"#,
2234 r#"
2235fn foo() {
2236 let k = 3;
2237 fun_name();
2238 let g = 5;
2239}
2240
2241fn $0fun_name() {
2242 let m = 1;
2243 let n = m + 1;
2244}
2245"#,
2246 );
2247 }
2248
2249 #[test]
2250 fn no_args_if() {
2251 check_assist(
2252 extract_function,
2253 r#"
2254fn foo() {
2255 $0if true { }$0
2256}
2257"#,
2258 r#"
2259fn foo() {
2260 fun_name();
2261}
2262
2263fn $0fun_name() {
2264 if true { }
2265}
2266"#,
2267 );
2268 }
2269
2270 #[test]
2271 fn no_args_if_else() {
2272 check_assist(
2273 extract_function,
2274 r#"
2275fn foo() -> i32 {
2276 $0if true { 1 } else { 2 }$0
2277}
2278"#,
2279 r#"
2280fn foo() -> i32 {
2281 fun_name()
2282}
2283
2284fn $0fun_name() -> i32 {
2285 if true { 1 } else { 2 }
2286}
2287"#,
2288 );
2289 }
2290
2291 #[test]
2292 fn no_args_if_let_else() {
2293 check_assist(
2294 extract_function,
2295 r#"
2296fn foo() -> i32 {
2297 $0if let true = false { 1 } else { 2 }$0
2298}
2299"#,
2300 r#"
2301fn foo() -> i32 {
2302 fun_name()
2303}
2304
2305fn $0fun_name() -> i32 {
2306 if let true = false { 1 } else { 2 }
2307}
2308"#,
2309 );
2310 }
2311
2312 #[test]
2313 fn no_args_match() {
2314 check_assist(
2315 extract_function,
2316 r#"
2317fn foo() -> i32 {
2318 $0match true {
2319 true => 1,
2320 false => 2,
2321 }$0
2322}
2323"#,
2324 r#"
2325fn foo() -> i32 {
2326 fun_name()
2327}
2328
2329fn $0fun_name() -> i32 {
2330 match true {
2331 true => 1,
2332 false => 2,
2333 }
2334}
2335"#,
2336 );
2337 }
2338
2339 #[test]
2340 fn no_args_while() {
2341 check_assist(
2342 extract_function,
2343 r#"
2344fn foo() {
2345 $0while true { }$0
2346}
2347"#,
2348 r#"
2349fn foo() {
2350 fun_name();
2351}
2352
2353fn $0fun_name() {
2354 while true { }
2355}
2356"#,
2357 );
2358 }
2359
2360 #[test]
2361 fn no_args_for() {
2362 check_assist(
2363 extract_function,
2364 r#"
2365fn foo() {
2366 $0for v in &[0, 1] { }$0
2367}
2368"#,
2369 r#"
2370fn foo() {
2371 fun_name();
2372}
2373
2374fn $0fun_name() {
2375 for v in &[0, 1] { }
2376}
2377"#,
2378 );
2379 }
2380
2381 #[test]
2382 fn no_args_from_loop_unit() {
2383 check_assist(
2384 extract_function,
2385 r#"
2386fn foo() {
2387 $0loop {
2388 let m = 1;
2389 }$0
2390}
2391"#,
2392 r#"
2393fn foo() {
2394 fun_name()
2395}
2396
2397fn $0fun_name() -> ! {
2398 loop {
2399 let m = 1;
2400 }
2401}
2402"#,
2403 );
2404 }
2405
2406 #[test]
2407 fn no_args_from_loop_with_return() {
2408 check_assist(
2409 extract_function,
2410 r#"
2411fn foo() {
2412 let v = $0loop {
2413 let m = 1;
2414 break m;
2415 }$0;
2416}
2417"#,
2418 r#"
2419fn foo() {
2420 let v = fun_name();
2421}
2422
2423fn $0fun_name() -> i32 {
2424 loop {
2425 let m = 1;
2426 break m;
2427 }
2428}
2429"#,
2430 );
2431 }
2432
2433 #[test]
2434 fn no_args_from_match() {
2435 check_assist(
2436 extract_function,
2437 r#"
2438fn foo() {
2439 let v: i32 = $0match Some(1) {
2440 Some(x) => x,
2441 None => 0,
2442 }$0;
2443}
2444"#,
2445 r#"
2446fn foo() {
2447 let v: i32 = fun_name();
2448}
2449
2450fn $0fun_name() -> i32 {
2451 match Some(1) {
2452 Some(x) => x,
2453 None => 0,
2454 }
2455}
2456"#,
2457 );
2458 }
2459
2460 #[test]
2461 fn extract_partial_block_single_line() {
2462 check_assist(
2463 extract_function,
2464 r#"
2465fn foo() {
2466 let n = 1;
2467 let mut v = $0n * n;$0
2468 v += 1;
2469}
2470"#,
2471 r#"
2472fn foo() {
2473 let n = 1;
2474 let mut v = fun_name(n);
2475 v += 1;
2476}
2477
2478fn $0fun_name(n: i32) -> i32 {
2479 let mut v = n * n;
2480 v
2481}
2482"#,
2483 );
2484 }
2485
2486 #[test]
2487 fn extract_partial_block() {
2488 check_assist(
2489 extract_function,
2490 r#"
2491fn foo() {
2492 let m = 2;
2493 let n = 1;
2494 let mut v = m $0* n;
2495 let mut w = 3;$0
2496 v += 1;
2497 w += 1;
2498}
2499"#,
2500 r#"
2501fn foo() {
2502 let m = 2;
2503 let n = 1;
2504 let (mut v, mut w) = fun_name(m, n);
2505 v += 1;
2506 w += 1;
2507}
2508
2509fn $0fun_name(m: i32, n: i32) -> (i32, i32) {
2510 let mut v = m * n;
2511 let mut w = 3;
2512 (v, w)
2513}
2514"#,
2515 );
2516 }
2517
2518 #[test]
2519 fn argument_form_expr() {
2520 check_assist(
2521 extract_function,
2522 r#"
2523fn foo() -> u32 {
2524 let n = 2;
2525 $0n+2$0
2526}
2527"#,
2528 r#"
2529fn foo() -> u32 {
2530 let n = 2;
2531 fun_name(n)
2532}
2533
2534fn $0fun_name(n: u32) -> u32 {
2535 n+2
2536}
2537"#,
2538 )
2539 }
2540
2541 #[test]
2542 fn argument_used_twice_form_expr() {
2543 check_assist(
2544 extract_function,
2545 r#"
2546fn foo() -> u32 {
2547 let n = 2;
2548 $0n+n$0
2549}
2550"#,
2551 r#"
2552fn foo() -> u32 {
2553 let n = 2;
2554 fun_name(n)
2555}
2556
2557fn $0fun_name(n: u32) -> u32 {
2558 n+n
2559}
2560"#,
2561 )
2562 }
2563
2564 #[test]
2565 fn two_arguments_form_expr() {
2566 check_assist(
2567 extract_function,
2568 r#"
2569fn foo() -> u32 {
2570 let n = 2;
2571 let m = 3;
2572 $0n+n*m$0
2573}
2574"#,
2575 r#"
2576fn foo() -> u32 {
2577 let n = 2;
2578 let m = 3;
2579 fun_name(n, m)
2580}
2581
2582fn $0fun_name(n: u32, m: u32) -> u32 {
2583 n+n*m
2584}
2585"#,
2586 )
2587 }
2588
2589 #[test]
2590 fn argument_and_locals() {
2591 check_assist(
2592 extract_function,
2593 r#"
2594fn foo() -> u32 {
2595 let n = 2;
2596 $0let m = 1;
2597 n + m$0
2598}
2599"#,
2600 r#"
2601fn foo() -> u32 {
2602 let n = 2;
2603 fun_name(n)
2604}
2605
2606fn $0fun_name(n: u32) -> u32 {
2607 let m = 1;
2608 n + m
2609}
2610"#,
2611 )
2612 }
2613
2614 #[test]
2615 fn in_comment_is_not_applicable() {
2616 cov_mark::check!(extract_function_in_comment_is_not_applicable);
2617 check_assist_not_applicable(extract_function, r"fn main() { 1 + /* $0comment$0 */ 1; }");
2618 }
2619
2620 #[test]
2621 fn empty_selection_is_not_applicable() {
2622 cov_mark::check!(extract_function_empty_selection_is_not_applicable);
2623 check_assist_not_applicable(
2624 extract_function,
2625 r#"
2626fn main() {
2627 $0
2628
2629 $0
2630}"#,
2631 );
2632 }
2633
2634 #[test]
2635 fn part_of_expr_stmt() {
2636 check_assist(
2637 extract_function,
2638 r#"
2639fn foo() {
2640 $01$0 + 1;
2641}
2642"#,
2643 r#"
2644fn foo() {
2645 fun_name() + 1;
2646}
2647
2648fn $0fun_name() -> i32 {
2649 1
2650}
2651"#,
2652 );
2653 }
2654
2655 #[test]
2656 fn function_expr() {
2657 check_assist(
2658 extract_function,
2659 r#"
2660fn foo() {
2661 $0bar(1 + 1)$0
2662}
2663"#,
2664 r#"
2665fn foo() {
2666 fun_name();
2667}
2668
2669fn $0fun_name() {
2670 bar(1 + 1)
2671}
2672"#,
2673 )
2674 }
2675
2676 #[test]
2677 fn extract_from_nested() {
2678 check_assist(
2679 extract_function,
2680 r#"
2681fn main() {
2682 let x = true;
2683 let tuple = match x {
2684 true => ($02 + 2$0, true)
2685 _ => (0, false)
2686 };
2687}
2688"#,
2689 r#"
2690fn main() {
2691 let x = true;
2692 let tuple = match x {
2693 true => (fun_name(), true)
2694 _ => (0, false)
2695 };
2696}
2697
2698fn $0fun_name() -> i32 {
2699 2 + 2
2700}
2701"#,
2702 );
2703 }
2704
2705 #[test]
2706 fn param_from_closure() {
2707 check_assist(
2708 extract_function,
2709 r#"
2710fn main() {
2711 let lambda = |x: u32| $0x * 2$0;
2712}
2713"#,
2714 r#"
2715fn main() {
2716 let lambda = |x: u32| fun_name(x);
2717}
2718
2719fn $0fun_name(x: u32) -> u32 {
2720 x * 2
2721}
2722"#,
2723 );
2724 }
2725
2726 #[test]
2727 fn extract_return_stmt() {
2728 check_assist(
2729 extract_function,
2730 r#"
2731fn foo() -> u32 {
2732 $0return 2 + 2$0;
2733}
2734"#,
2735 r#"
2736fn foo() -> u32 {
2737 return fun_name();
2738}
2739
2740fn $0fun_name() -> u32 {
2741 2 + 2
2742}
2743"#,
2744 );
2745 }
2746
2747 #[test]
2748 fn does_not_add_extra_whitespace() {
2749 check_assist(
2750 extract_function,
2751 r#"
2752fn foo() -> u32 {
2753
2754
2755 $0return 2 + 2$0;
2756}
2757"#,
2758 r#"
2759fn foo() -> u32 {
2760
2761
2762 return fun_name();
2763}
2764
2765fn $0fun_name() -> u32 {
2766 2 + 2
2767}
2768"#,
2769 );
2770 }
2771
2772 #[test]
2773 fn break_stmt() {
2774 check_assist(
2775 extract_function,
2776 r#"
2777fn main() {
2778 let result = loop {
2779 $0break 2 + 2$0;
2780 };
2781}
2782"#,
2783 r#"
2784fn main() {
2785 let result = loop {
2786 break fun_name();
2787 };
2788}
2789
2790fn $0fun_name() -> i32 {
2791 2 + 2
2792}
2793"#,
2794 );
2795 }
2796
2797 #[test]
2798 fn extract_cast() {
2799 check_assist(
2800 extract_function,
2801 r#"
2802fn main() {
2803 let v = $00f32 as u32$0;
2804}
2805"#,
2806 r#"
2807fn main() {
2808 let v = fun_name();
2809}
2810
2811fn $0fun_name() -> u32 {
2812 0f32 as u32
2813}
2814"#,
2815 );
2816 }
2817
2818 #[test]
2819 fn return_not_applicable() {
2820 check_assist_not_applicable(extract_function, r"fn foo() { $0return$0; } ");
2821 }
2822
2823 #[test]
2824 fn method_to_freestanding() {
2825 check_assist(
2826 extract_function,
2827 r#"
2828struct S;
2829
2830impl S {
2831 fn foo(&self) -> i32 {
2832 $01+1$0
2833 }
2834}
2835"#,
2836 r#"
2837struct S;
2838
2839impl S {
2840 fn foo(&self) -> i32 {
2841 fun_name()
2842 }
2843}
2844
2845fn $0fun_name() -> i32 {
2846 1+1
2847}
2848"#,
2849 );
2850 }
2851
2852 #[test]
2853 fn method_with_reference() {
2854 check_assist(
2855 extract_function,
2856 r#"
2857struct S { f: i32 };
2858
2859impl S {
2860 fn foo(&self) -> i32 {
2861 $0self.f+self.f$0
2862 }
2863}
2864"#,
2865 r#"
2866struct S { f: i32 };
2867
2868impl S {
2869 fn foo(&self) -> i32 {
2870 self.fun_name()
2871 }
2872
2873 fn $0fun_name(&self) -> i32 {
2874 self.f+self.f
2875 }
2876}
2877"#,
2878 );
2879 }
2880
2881 #[test]
2882 fn method_with_mut() {
2883 check_assist(
2884 extract_function,
2885 r#"
2886struct S { f: i32 };
2887
2888impl S {
2889 fn foo(&mut self) {
2890 $0self.f += 1;$0
2891 }
2892}
2893"#,
2894 r#"
2895struct S { f: i32 };
2896
2897impl S {
2898 fn foo(&mut self) {
2899 self.fun_name();
2900 }
2901
2902 fn $0fun_name(&mut self) {
2903 self.f += 1;
2904 }
2905}
2906"#,
2907 );
2908 }
2909
2910 #[test]
2911 fn variable_defined_inside_and_used_after_no_ret() {
2912 check_assist(
2913 extract_function,
2914 r#"
2915fn foo() {
2916 let n = 1;
2917 $0let k = n * n;$0
2918 let m = k + 1;
2919}
2920"#,
2921 r#"
2922fn foo() {
2923 let n = 1;
2924 let k = fun_name(n);
2925 let m = k + 1;
2926}
2927
2928fn $0fun_name(n: i32) -> i32 {
2929 let k = n * n;
2930 k
2931}
2932"#,
2933 );
2934 }
2935
2936 #[test]
2937 fn variable_defined_inside_and_used_after_mutably_no_ret() {
2938 check_assist(
2939 extract_function,
2940 r#"
2941fn foo() {
2942 let n = 1;
2943 $0let mut k = n * n;$0
2944 k += 1;
2945}
2946"#,
2947 r#"
2948fn foo() {
2949 let n = 1;
2950 let mut k = fun_name(n);
2951 k += 1;
2952}
2953
2954fn $0fun_name(n: i32) -> i32 {
2955 let mut k = n * n;
2956 k
2957}
2958"#,
2959 );
2960 }
2961
2962 #[test]
2963 fn two_variables_defined_inside_and_used_after_no_ret() {
2964 check_assist(
2965 extract_function,
2966 r#"
2967fn foo() {
2968 let n = 1;
2969 $0let k = n * n;
2970 let m = k + 2;$0
2971 let h = k + m;
2972}
2973"#,
2974 r#"
2975fn foo() {
2976 let n = 1;
2977 let (k, m) = fun_name(n);
2978 let h = k + m;
2979}
2980
2981fn $0fun_name(n: i32) -> (i32, i32) {
2982 let k = n * n;
2983 let m = k + 2;
2984 (k, m)
2985}
2986"#,
2987 );
2988 }
2989
2990 #[test]
2991 fn multi_variables_defined_inside_and_used_after_mutably_no_ret() {
2992 check_assist(
2993 extract_function,
2994 r#"
2995fn foo() {
2996 let n = 1;
2997 $0let mut k = n * n;
2998 let mut m = k + 2;
2999 let mut o = m + 3;
3000 o += 1;$0
3001 k += o;
3002 m = 1;
3003}
3004"#,
3005 r#"
3006fn foo() {
3007 let n = 1;
3008 let (mut k, mut m, o) = fun_name(n);
3009 k += o;
3010 m = 1;
3011}
3012
3013fn $0fun_name(n: i32) -> (i32, i32, i32) {
3014 let mut k = n * n;
3015 let mut m = k + 2;
3016 let mut o = m + 3;
3017 o += 1;
3018 (k, m, o)
3019}
3020"#,
3021 );
3022 }
3023
3024 #[test]
3025 fn nontrivial_patterns_define_variables() {
3026 check_assist(
3027 extract_function,
3028 r#"
3029struct Counter(i32);
3030fn foo() {
3031 $0let Counter(n) = Counter(0);$0
3032 let m = n;
3033}
3034"#,
3035 r#"
3036struct Counter(i32);
3037fn foo() {
3038 let n = fun_name();
3039 let m = n;
3040}
3041
3042fn $0fun_name() -> i32 {
3043 let Counter(n) = Counter(0);
3044 n
3045}
3046"#,
3047 );
3048 }
3049
3050 #[test]
3051 fn struct_with_two_fields_pattern_define_variables() {
3052 check_assist(
3053 extract_function,
3054 r#"
3055struct Counter { n: i32, m: i32 };
3056fn foo() {
3057 $0let Counter { n, m: k } = Counter { n: 1, m: 2 };$0
3058 let h = n + k;
3059}
3060"#,
3061 r#"
3062struct Counter { n: i32, m: i32 };
3063fn foo() {
3064 let (n, k) = fun_name();
3065 let h = n + k;
3066}
3067
3068fn $0fun_name() -> (i32, i32) {
3069 let Counter { n, m: k } = Counter { n: 1, m: 2 };
3070 (n, k)
3071}
3072"#,
3073 );
3074 }
3075
3076 #[test]
3077 fn mut_var_from_outer_scope() {
3078 check_assist(
3079 extract_function,
3080 r#"
3081fn foo() {
3082 let mut n = 1;
3083 $0n += 1;$0
3084 let m = n + 1;
3085}
3086"#,
3087 r#"
3088fn foo() {
3089 let mut n = 1;
3090 fun_name(&mut n);
3091 let m = n + 1;
3092}
3093
3094fn $0fun_name(n: &mut i32) {
3095 *n += 1;
3096}
3097"#,
3098 );
3099 }
3100
3101 #[test]
3102 fn mut_field_from_outer_scope() {
3103 check_assist(
3104 extract_function,
3105 r#"
3106struct C { n: i32 }
3107fn foo() {
3108 let mut c = C { n: 0 };
3109 $0c.n += 1;$0
3110 let m = c.n + 1;
3111}
3112"#,
3113 r#"
3114struct C { n: i32 }
3115fn foo() {
3116 let mut c = C { n: 0 };
3117 fun_name(&mut c);
3118 let m = c.n + 1;
3119}
3120
3121fn $0fun_name(c: &mut C) {
3122 c.n += 1;
3123}
3124"#,
3125 );
3126 }
3127
3128 #[test]
3129 fn mut_nested_field_from_outer_scope() {
3130 check_assist(
3131 extract_function,
3132 r#"
3133struct P { n: i32}
3134struct C { p: P }
3135fn foo() {
3136 let mut c = C { p: P { n: 0 } };
3137 let mut v = C { p: P { n: 0 } };
3138 let u = C { p: P { n: 0 } };
3139 $0c.p.n += u.p.n;
3140 let r = &mut v.p.n;$0
3141 let m = c.p.n + v.p.n + u.p.n;
3142}
3143"#,
3144 r#"
3145struct P { n: i32}
3146struct C { p: P }
3147fn foo() {
3148 let mut c = C { p: P { n: 0 } };
3149 let mut v = C { p: P { n: 0 } };
3150 let u = C { p: P { n: 0 } };
3151 fun_name(&mut c, &mut v, &u);
3152 let m = c.p.n + v.p.n + u.p.n;
3153}
3154
3155fn $0fun_name(c: &mut C, v: &mut C, u: &C) {
3156 c.p.n += u.p.n;
3157 let r = &mut v.p.n;
3158}
3159"#,
3160 );
3161 }
3162
3163 #[test]
3164 fn mut_param_many_usages_stmt() {
3165 check_assist(
3166 extract_function,
3167 r#"
3168fn bar(k: i32) {}
3169trait I: Copy {
3170 fn succ(&self) -> Self;
3171 fn inc(&mut self) -> Self { let v = self.succ(); *self = v; v }
3172}
3173impl I for i32 {
3174 fn succ(&self) -> Self { *self + 1 }
3175}
3176fn foo() {
3177 let mut n = 1;
3178 $0n += n;
3179 bar(n);
3180 bar(n+1);
3181 bar(n*n);
3182 bar(&n);
3183 n.inc();
3184 let v = &mut n;
3185 *v = v.succ();
3186 n.succ();$0
3187 let m = n + 1;
3188}
3189"#,
3190 r#"
3191fn bar(k: i32) {}
3192trait I: Copy {
3193 fn succ(&self) -> Self;
3194 fn inc(&mut self) -> Self { let v = self.succ(); *self = v; v }
3195}
3196impl I for i32 {
3197 fn succ(&self) -> Self { *self + 1 }
3198}
3199fn foo() {
3200 let mut n = 1;
3201 fun_name(&mut n);
3202 let m = n + 1;
3203}
3204
3205fn $0fun_name(n: &mut i32) {
3206 *n += *n;
3207 bar(*n);
3208 bar(*n+1);
3209 bar(*n**n);
3210 bar(&*n);
3211 n.inc();
3212 let v = n;
3213 *v = v.succ();
3214 n.succ();
3215}
3216"#,
3217 );
3218 }
3219
3220 #[test]
3221 fn mut_param_many_usages_expr() {
3222 check_assist(
3223 extract_function,
3224 r#"
3225fn bar(k: i32) {}
3226trait I: Copy {
3227 fn succ(&self) -> Self;
3228 fn inc(&mut self) -> Self { let v = self.succ(); *self = v; v }
3229}
3230impl I for i32 {
3231 fn succ(&self) -> Self { *self + 1 }
3232}
3233fn foo() {
3234 let mut n = 1;
3235 $0{
3236 n += n;
3237 bar(n);
3238 bar(n+1);
3239 bar(n*n);
3240 bar(&n);
3241 n.inc();
3242 let v = &mut n;
3243 *v = v.succ();
3244 n.succ();
3245 }$0
3246 let m = n + 1;
3247}
3248"#,
3249 r#"
3250fn bar(k: i32) {}
3251trait I: Copy {
3252 fn succ(&self) -> Self;
3253 fn inc(&mut self) -> Self { let v = self.succ(); *self = v; v }
3254}
3255impl I for i32 {
3256 fn succ(&self) -> Self { *self + 1 }
3257}
3258fn foo() {
3259 let mut n = 1;
3260 fun_name(&mut n);
3261 let m = n + 1;
3262}
3263
3264fn $0fun_name(n: &mut i32) {
3265 *n += *n;
3266 bar(*n);
3267 bar(*n+1);
3268 bar(*n**n);
3269 bar(&*n);
3270 n.inc();
3271 let v = n;
3272 *v = v.succ();
3273 n.succ();
3274}
3275"#,
3276 );
3277 }
3278
3279 #[test]
3280 fn mut_param_by_value() {
3281 check_assist(
3282 extract_function,
3283 r#"
3284fn foo() {
3285 let mut n = 1;
3286 $0n += 1;$0
3287}
3288"#,
3289 r"
3290fn foo() {
3291 let mut n = 1;
3292 fun_name(n);
3293}
3294
3295fn $0fun_name(mut n: i32) {
3296 n += 1;
3297}
3298",
3299 );
3300 }
3301
3302 #[test]
3303 fn mut_param_because_of_mut_ref() {
3304 check_assist(
3305 extract_function,
3306 r#"
3307fn foo() {
3308 let mut n = 1;
3309 $0let v = &mut n;
3310 *v += 1;$0
3311 let k = n;
3312}
3313"#,
3314 r#"
3315fn foo() {
3316 let mut n = 1;
3317 fun_name(&mut n);
3318 let k = n;
3319}
3320
3321fn $0fun_name(n: &mut i32) {
3322 let v = n;
3323 *v += 1;
3324}
3325"#,
3326 );
3327 }
3328
3329 #[test]
3330 fn mut_param_by_value_because_of_mut_ref() {
3331 check_assist(
3332 extract_function,
3333 r"
3334fn foo() {
3335 let mut n = 1;
3336 $0let v = &mut n;
3337 *v += 1;$0
3338}
3339",
3340 r#"
3341fn foo() {
3342 let mut n = 1;
3343 fun_name(n);
3344}
3345
3346fn $0fun_name(mut n: i32) {
3347 let v = &mut n;
3348 *v += 1;
3349}
3350"#,
3351 );
3352 }
3353
3354 #[test]
3355 fn mut_method_call() {
3356 check_assist(
3357 extract_function,
3358 r#"
3359trait I {
3360 fn inc(&mut self);
3361}
3362impl I for i32 {
3363 fn inc(&mut self) { *self += 1 }
3364}
3365fn foo() {
3366 let mut n = 1;
3367 $0n.inc();$0
3368}
3369"#,
3370 r#"
3371trait I {
3372 fn inc(&mut self);
3373}
3374impl I for i32 {
3375 fn inc(&mut self) { *self += 1 }
3376}
3377fn foo() {
3378 let mut n = 1;
3379 fun_name(n);
3380}
3381
3382fn $0fun_name(mut n: i32) {
3383 n.inc();
3384}
3385"#,
3386 );
3387 }
3388
3389 #[test]
3390 fn shared_method_call() {
3391 check_assist(
3392 extract_function,
3393 r#"
3394trait I {
3395 fn succ(&self);
3396}
3397impl I for i32 {
3398 fn succ(&self) { *self + 1 }
3399}
3400fn foo() {
3401 let mut n = 1;
3402 $0n.succ();$0
3403}
3404"#,
3405 r"
3406trait I {
3407 fn succ(&self);
3408}
3409impl I for i32 {
3410 fn succ(&self) { *self + 1 }
3411}
3412fn foo() {
3413 let mut n = 1;
3414 fun_name(n);
3415}
3416
3417fn $0fun_name(n: i32) {
3418 n.succ();
3419}
3420",
3421 );
3422 }
3423
3424 #[test]
3425 fn mut_method_call_with_other_receiver() {
3426 check_assist(
3427 extract_function,
3428 r#"
3429trait I {
3430 fn inc(&mut self, n: i32);
3431}
3432impl I for i32 {
3433 fn inc(&mut self, n: i32) { *self += n }
3434}
3435fn foo() {
3436 let mut n = 1;
3437 $0let mut m = 2;
3438 m.inc(n);$0
3439}
3440"#,
3441 r"
3442trait I {
3443 fn inc(&mut self, n: i32);
3444}
3445impl I for i32 {
3446 fn inc(&mut self, n: i32) { *self += n }
3447}
3448fn foo() {
3449 let mut n = 1;
3450 fun_name(n);
3451}
3452
3453fn $0fun_name(n: i32) {
3454 let mut m = 2;
3455 m.inc(n);
3456}
3457",
3458 );
3459 }
3460
3461 #[test]
3462 fn non_copy_without_usages_after() {
3463 check_assist(
3464 extract_function,
3465 r#"
3466struct Counter(i32);
3467fn foo() {
3468 let c = Counter(0);
3469 $0let n = c.0;$0
3470}
3471"#,
3472 r"
3473struct Counter(i32);
3474fn foo() {
3475 let c = Counter(0);
3476 fun_name(c);
3477}
3478
3479fn $0fun_name(c: Counter) {
3480 let n = c.0;
3481}
3482",
3483 );
3484 }
3485
3486 #[test]
3487 fn non_copy_used_after() {
3488 check_assist(
3489 extract_function,
3490 r"
3491struct Counter(i32);
3492fn foo() {
3493 let c = Counter(0);
3494 $0let n = c.0;$0
3495 let m = c.0;
3496}
3497",
3498 r#"
3499struct Counter(i32);
3500fn foo() {
3501 let c = Counter(0);
3502 fun_name(&c);
3503 let m = c.0;
3504}
3505
3506fn $0fun_name(c: &Counter) {
3507 let n = c.0;
3508}
3509"#,
3510 );
3511 }
3512
3513 #[test]
3514 fn copy_used_after() {
3515 check_assist(
3516 extract_function,
3517 r#"
3518//- minicore: copy
3519fn foo() {
3520 let n = 0;
3521 $0let m = n;$0
3522 let k = n;
3523}
3524"#,
3525 r#"
3526fn foo() {
3527 let n = 0;
3528 fun_name(n);
3529 let k = n;
3530}
3531
3532fn $0fun_name(n: i32) {
3533 let m = n;
3534}
3535"#,
3536 )
3537 }
3538
3539 #[test]
3540 fn copy_custom_used_after() {
3541 check_assist(
3542 extract_function,
3543 r#"
3544//- minicore: copy, derive
3545#[derive(Clone, Copy)]
3546struct Counter(i32);
3547fn foo() {
3548 let c = Counter(0);
3549 $0let n = c.0;$0
3550 let m = c.0;
3551}
3552"#,
3553 r#"
3554#[derive(Clone, Copy)]
3555struct Counter(i32);
3556fn foo() {
3557 let c = Counter(0);
3558 fun_name(c);
3559 let m = c.0;
3560}
3561
3562fn $0fun_name(c: Counter) {
3563 let n = c.0;
3564}
3565"#,
3566 );
3567 }
3568
3569 #[test]
3570 fn indented_stmts() {
3571 check_assist(
3572 extract_function,
3573 r#"
3574fn foo() {
3575 if true {
3576 loop {
3577 $0let n = 1;
3578 let m = 2;$0
3579 }
3580 }
3581}
3582"#,
3583 r#"
3584fn foo() {
3585 if true {
3586 loop {
3587 fun_name();
3588 }
3589 }
3590}
3591
3592fn $0fun_name() {
3593 let n = 1;
3594 let m = 2;
3595}
3596"#,
3597 );
3598 }
3599
3600 #[test]
3601 fn indented_stmts_inside_mod() {
3602 check_assist(
3603 extract_function,
3604 r#"
3605mod bar {
3606 fn foo() {
3607 if true {
3608 loop {
3609 $0let n = 1;
3610 let m = 2;$0
3611 }
3612 }
3613 }
3614}
3615"#,
3616 r#"
3617mod bar {
3618 fn foo() {
3619 if true {
3620 loop {
3621 fun_name();
3622 }
3623 }
3624 }
3625
3626 fn $0fun_name() {
3627 let n = 1;
3628 let m = 2;
3629 }
3630}
3631"#,
3632 );
3633 }
3634
3635 #[test]
3636 fn break_loop() {
3637 check_assist(
3638 extract_function,
3639 r#"
3640//- minicore: option
3641fn foo() {
3642 loop {
3643 let n = 1;
3644 $0let m = n + 1;
3645 break;
3646 let k = 2;$0
3647 let h = 1 + k;
3648 }
3649}
3650"#,
3651 r#"
3652fn foo() {
3653 loop {
3654 let n = 1;
3655 let k = match fun_name(n) {
3656 Some(value) => value,
3657 None => break,
3658 };
3659 let h = 1 + k;
3660 }
3661}
3662
3663fn $0fun_name(n: i32) -> Option<i32> {
3664 let m = n + 1;
3665 return None;
3666 let k = 2;
3667 Some(k)
3668}
3669"#,
3670 );
3671 }
3672
3673 #[test]
3674 fn return_to_parent() {
3675 check_assist(
3676 extract_function,
3677 r#"
3678//- minicore: copy, result
3679fn foo() -> i64 {
3680 let n = 1;
3681 $0let m = n + 1;
3682 return 1;
3683 let k = 2;$0
3684 (n + k) as i64
3685}
3686"#,
3687 r#"
3688fn foo() -> i64 {
3689 let n = 1;
3690 let k = match fun_name(n) {
3691 Ok(value) => value,
3692 Err(value) => return value,
3693 };
3694 (n + k) as i64
3695}
3696
3697fn $0fun_name(n: i32) -> Result<i32, i64> {
3698 let m = n + 1;
3699 return Err(1);
3700 let k = 2;
3701 Ok(k)
3702}
3703"#,
3704 );
3705 }
3706
3707 #[test]
3708 fn break_and_continue() {
3709 cov_mark::check!(external_control_flow_break_and_continue);
3710 check_assist_not_applicable(
3711 extract_function,
3712 r#"
3713fn foo() {
3714 loop {
3715 let n = 1;
3716 $0let m = n + 1;
3717 break;
3718 let k = 2;
3719 continue;
3720 let k = k + 1;$0
3721 let r = n + k;
3722 }
3723}
3724"#,
3725 );
3726 }
3727
3728 #[test]
3729 fn return_and_break() {
3730 cov_mark::check!(external_control_flow_return_and_bc);
3731 check_assist_not_applicable(
3732 extract_function,
3733 r#"
3734fn foo() {
3735 loop {
3736 let n = 1;
3737 $0let m = n + 1;
3738 break;
3739 let k = 2;
3740 return;
3741 let k = k + 1;$0
3742 let r = n + k;
3743 }
3744}
3745"#,
3746 );
3747 }
3748
3749 #[test]
3750 fn break_loop_with_if() {
3751 check_assist(
3752 extract_function,
3753 r#"
3754//- minicore: try
3755fn foo() {
3756 loop {
3757 let mut n = 1;
3758 $0let m = n + 1;
3759 break;
3760 n += m;$0
3761 let h = 1 + n;
3762 }
3763}
3764"#,
3765 r#"
3766use core::ops::ControlFlow;
3767
3768fn foo() {
3769 loop {
3770 let mut n = 1;
3771 if let ControlFlow::Break(_) = fun_name(&mut n) {
3772 break;
3773 }
3774 let h = 1 + n;
3775 }
3776}
3777
3778fn $0fun_name(n: &mut i32) -> ControlFlow<()> {
3779 let m = *n + 1;
3780 return ControlFlow::Break(());
3781 *n += m;
3782 ControlFlow::Continue(())
3783}
3784"#,
3785 );
3786 }
3787
3788 #[test]
3789 fn break_loop_nested() {
3790 check_assist(
3791 extract_function,
3792 r#"
3793//- minicore: try
3794fn foo() {
3795 loop {
3796 let mut n = 1;
3797 $0let m = n + 1;
3798 if m == 42 {
3799 break;
3800 }$0
3801 let h = 1;
3802 }
3803}
3804"#,
3805 r#"
3806use core::ops::ControlFlow;
3807
3808fn foo() {
3809 loop {
3810 let mut n = 1;
3811 if let ControlFlow::Break(_) = fun_name(n) {
3812 break;
3813 }
3814 let h = 1;
3815 }
3816}
3817
3818fn $0fun_name(n: i32) -> ControlFlow<()> {
3819 let m = n + 1;
3820 if m == 42 {
3821 return ControlFlow::Break(());
3822 }
3823 ControlFlow::Continue(())
3824}
3825"#,
3826 );
3827 }
3828
3829 #[test]
3830 fn break_loop_nested_labeled() {
3831 check_assist(
3832 extract_function,
3833 r#"
3834//- minicore: try
3835fn foo() {
3836 'bar: loop {
3837 loop {
3838 $0break 'bar;$0
3839 }
3840 }
3841}
3842"#,
3843 r#"
3844use core::ops::ControlFlow;
3845
3846fn foo() {
3847 'bar: loop {
3848 loop {
3849 if let ControlFlow::Break(_) = fun_name() {
3850 break 'bar;
3851 }
3852 }
3853 }
3854}
3855
3856fn $0fun_name() -> ControlFlow<()> {
3857 return ControlFlow::Break(());
3858 ControlFlow::Continue(())
3859}
3860"#,
3861 );
3862 }
3863
3864 #[test]
3865 fn continue_loop_nested_labeled() {
3866 check_assist(
3867 extract_function,
3868 r#"
3869//- minicore: try
3870fn foo() {
3871 'bar: loop {
3872 loop {
3873 $0continue 'bar;$0
3874 }
3875 }
3876}
3877"#,
3878 r#"
3879use core::ops::ControlFlow;
3880
3881fn foo() {
3882 'bar: loop {
3883 loop {
3884 if let ControlFlow::Break(_) = fun_name() {
3885 continue 'bar;
3886 }
3887 }
3888 }
3889}
3890
3891fn $0fun_name() -> ControlFlow<()> {
3892 return ControlFlow::Break(());
3893 ControlFlow::Continue(())
3894}
3895"#,
3896 );
3897 }
3898
3899 #[test]
3900 fn return_from_nested_loop() {
3901 check_assist(
3902 extract_function,
3903 r#"
3904fn foo() {
3905 loop {
3906 let n = 1;$0
3907 let k = 1;
3908 loop {
3909 return;
3910 }
3911 let m = k + 1;$0
3912 let h = 1 + m;
3913 }
3914}
3915"#,
3916 r#"
3917fn foo() {
3918 loop {
3919 let n = 1;
3920 let m = match fun_name() {
3921 Some(value) => value,
3922 None => return,
3923 };
3924 let h = 1 + m;
3925 }
3926}
3927
3928fn $0fun_name() -> Option<i32> {
3929 let k = 1;
3930 loop {
3931 return None;
3932 }
3933 let m = k + 1;
3934 Some(m)
3935}
3936"#,
3937 );
3938 }
3939
3940 #[test]
3941 fn break_from_nested_loop() {
3942 check_assist(
3943 extract_function,
3944 r#"
3945fn foo() {
3946 loop {
3947 let n = 1;
3948 $0let k = 1;
3949 loop {
3950 break;
3951 }
3952 let m = k + 1;$0
3953 let h = 1 + m;
3954 }
3955}
3956"#,
3957 r#"
3958fn foo() {
3959 loop {
3960 let n = 1;
3961 let m = fun_name();
3962 let h = 1 + m;
3963 }
3964}
3965
3966fn $0fun_name() -> i32 {
3967 let k = 1;
3968 loop {
3969 break;
3970 }
3971 let m = k + 1;
3972 m
3973}
3974"#,
3975 );
3976 }
3977
3978 #[test]
3979 fn break_from_nested_and_outer_loops() {
3980 check_assist(
3981 extract_function,
3982 r#"
3983fn foo() {
3984 loop {
3985 let n = 1;
3986 $0let k = 1;
3987 loop {
3988 break;
3989 }
3990 if k == 42 {
3991 break;
3992 }
3993 let m = k + 1;$0
3994 let h = 1 + m;
3995 }
3996}
3997"#,
3998 r#"
3999fn foo() {
4000 loop {
4001 let n = 1;
4002 let m = match fun_name() {
4003 Some(value) => value,
4004 None => break,
4005 };
4006 let h = 1 + m;
4007 }
4008}
4009
4010fn $0fun_name() -> Option<i32> {
4011 let k = 1;
4012 loop {
4013 break;
4014 }
4015 if k == 42 {
4016 return None;
4017 }
4018 let m = k + 1;
4019 Some(m)
4020}
4021"#,
4022 );
4023 }
4024
4025 #[test]
4026 fn return_from_nested_fn() {
4027 check_assist(
4028 extract_function,
4029 r#"
4030fn foo() {
4031 loop {
4032 let n = 1;
4033 $0let k = 1;
4034 fn test() {
4035 return;
4036 }
4037 let m = k + 1;$0
4038 let h = 1 + m;
4039 }
4040}
4041"#,
4042 r#"
4043fn foo() {
4044 loop {
4045 let n = 1;
4046 let m = fun_name();
4047 let h = 1 + m;
4048 }
4049}
4050
4051fn $0fun_name() -> i32 {
4052 let k = 1;
4053 fn test() {
4054 return;
4055 }
4056 let m = k + 1;
4057 m
4058}
4059"#,
4060 );
4061 }
4062
4063 #[test]
4064 fn break_with_value() {
4065 check_assist(
4066 extract_function,
4067 r#"
4068fn foo() -> i32 {
4069 loop {
4070 let n = 1;
4071 $0let k = 1;
4072 if k == 42 {
4073 break 3;
4074 }
4075 let m = k + 1;$0
4076 let h = 1;
4077 }
4078}
4079"#,
4080 r#"
4081fn foo() -> i32 {
4082 loop {
4083 let n = 1;
4084 if let Some(value) = fun_name() {
4085 break value;
4086 }
4087 let h = 1;
4088 }
4089}
4090
4091fn $0fun_name() -> Option<i32> {
4092 let k = 1;
4093 if k == 42 {
4094 return Some(3);
4095 }
4096 let m = k + 1;
4097 None
4098}
4099"#,
4100 );
4101 }
4102
4103 #[test]
4104 fn break_with_value_and_label() {
4105 check_assist(
4106 extract_function,
4107 r#"
4108fn foo() -> i32 {
4109 'bar: loop {
4110 let n = 1;
4111 $0let k = 1;
4112 if k == 42 {
4113 break 'bar 4;
4114 }
4115 let m = k + 1;$0
4116 let h = 1;
4117 }
4118}
4119"#,
4120 r#"
4121fn foo() -> i32 {
4122 'bar: loop {
4123 let n = 1;
4124 if let Some(value) = fun_name() {
4125 break 'bar value;
4126 }
4127 let h = 1;
4128 }
4129}
4130
4131fn $0fun_name() -> Option<i32> {
4132 let k = 1;
4133 if k == 42 {
4134 return Some(4);
4135 }
4136 let m = k + 1;
4137 None
4138}
4139"#,
4140 );
4141 }
4142
4143 #[test]
4144 fn break_with_value_and_return() {
4145 check_assist(
4146 extract_function,
4147 r#"
4148fn foo() -> i64 {
4149 loop {
4150 let n = 1;$0
4151 let k = 1;
4152 if k == 42 {
4153 break 3;
4154 }
4155 let m = k + 1;$0
4156 let h = 1 + m;
4157 }
4158}
4159"#,
4160 r#"
4161fn foo() -> i64 {
4162 loop {
4163 let n = 1;
4164 let m = match fun_name() {
4165 Ok(value) => value,
4166 Err(value) => break value,
4167 };
4168 let h = 1 + m;
4169 }
4170}
4171
4172fn $0fun_name() -> Result<i32, i64> {
4173 let k = 1;
4174 if k == 42 {
4175 return Err(3);
4176 }
4177 let m = k + 1;
4178 Ok(m)
4179}
4180"#,
4181 );
4182 }
4183
4184 #[test]
4185 fn try_option() {
4186 check_assist(
4187 extract_function,
4188 r#"
4189//- minicore: option, add, builtin_impls
4190fn bar() -> Option<i32> { None }
4191fn foo() -> Option<()> {
4192 let n = bar()?;
4193 $0let k = foo()?;
4194 let m = k + 1;$0
4195 let h = 1 + m;
4196 Some(())
4197}
4198"#,
4199 r#"
4200fn bar() -> Option<i32> { None }
4201fn foo() -> Option<()> {
4202 let n = bar()?;
4203 let m = fun_name()?;
4204 let h = 1 + m;
4205 Some(())
4206}
4207
4208fn $0fun_name() -> Option<i32> {
4209 let k = foo()?;
4210 let m = k + 1;
4211 Some(m)
4212}
4213"#,
4214 );
4215 }
4216
4217 #[test]
4218 fn try_option_unit() {
4219 check_assist(
4220 extract_function,
4221 r#"
4222//- minicore: option
4223fn foo() -> Option<()> {
4224 let n = 1;
4225 $0let k = foo()?;
4226 let m = k + 1;$0
4227 let h = 1 + n;
4228 Some(())
4229}
4230"#,
4231 r#"
4232fn foo() -> Option<()> {
4233 let n = 1;
4234 fun_name()?;
4235 let h = 1 + n;
4236 Some(())
4237}
4238
4239fn $0fun_name() -> Option<()> {
4240 let k = foo()?;
4241 let m = k + 1;
4242 Some(())
4243}
4244"#,
4245 );
4246 }
4247
4248 #[test]
4249 fn try_result() {
4250 check_assist(
4251 extract_function,
4252 r#"
4253//- minicore: result, add, builtin_impls
4254fn foo() -> Result<(), i64> {
4255 let n = 1;
4256 $0let k = foo()?;
4257 let m = k + 1;$0
4258 let h = 1 + m;
4259 Ok(())
4260}
4261"#,
4262 r#"
4263fn foo() -> Result<(), i64> {
4264 let n = 1;
4265 let m = fun_name()?;
4266 let h = 1 + m;
4267 Ok(())
4268}
4269
4270fn $0fun_name() -> Result<i32, i64> {
4271 let k = foo()?;
4272 let m = k + 1;
4273 Ok(m)
4274}
4275"#,
4276 );
4277 }
4278
4279 #[test]
4280 fn try_option_with_return() {
4281 check_assist(
4282 extract_function,
4283 r#"
4284//- minicore: option, add, builtin_impls
4285fn foo() -> Option<()> {
4286 let n = 1;
4287 $0let k = foo()?;
4288 if k == 42 {
4289 return None;
4290 }
4291 let m = k + 1;$0
4292 let h = 1 + m;
4293 Some(())
4294}
4295"#,
4296 r#"
4297fn foo() -> Option<()> {
4298 let n = 1;
4299 let m = fun_name()?;
4300 let h = 1 + m;
4301 Some(())
4302}
4303
4304fn $0fun_name() -> Option<i32> {
4305 let k = foo()?;
4306 if k == 42 {
4307 return None;
4308 }
4309 let m = k + 1;
4310 Some(m)
4311}
4312"#,
4313 );
4314 }
4315
4316 #[test]
4317 fn try_result_with_return() {
4318 check_assist(
4319 extract_function,
4320 r#"
4321//- minicore: result, add, builtin_impls
4322fn foo() -> Result<(), i64> {
4323 let n = 1;
4324 $0let k = foo()?;
4325 if k == 42 {
4326 return Err(1);
4327 }
4328 let m = k + 1;$0
4329 let h = 1 + m;
4330 Ok(())
4331}
4332"#,
4333 r#"
4334fn foo() -> Result<(), i64> {
4335 let n = 1;
4336 let m = fun_name()?;
4337 let h = 1 + m;
4338 Ok(())
4339}
4340
4341fn $0fun_name() -> Result<i32, i64> {
4342 let k = foo()?;
4343 if k == 42 {
4344 return Err(1);
4345 }
4346 let m = k + 1;
4347 Ok(m)
4348}
4349"#,
4350 );
4351 }
4352
4353 #[test]
4354 fn try_and_break() {
4355 cov_mark::check!(external_control_flow_try_and_bc);
4356 check_assist_not_applicable(
4357 extract_function,
4358 r#"
4359//- minicore: option
4360fn foo() -> Option<()> {
4361 loop {
4362 let n = Some(1);
4363 $0let m = n? + 1;
4364 break;
4365 let k = 2;
4366 let k = k + 1;$0
4367 let r = n + k;
4368 }
4369 Some(())
4370}
4371"#,
4372 );
4373 }
4374
4375 #[test]
4376 fn try_and_return_ok() {
4377 check_assist(
4378 extract_function,
4379 r#"
4380//- minicore: result, add, builtin_impls
4381fn foo() -> Result<(), i64> {
4382 let n = 1;
4383 $0let k = foo()?;
4384 if k == 42 {
4385 return Ok(1);
4386 }
4387 let m = k + 1;$0
4388 let h = 1 + m;
4389 Ok(())
4390}
4391"#,
4392 r#"
4393fn foo() -> Result<(), i64> {
4394 let n = 1;
4395 let m = fun_name()?;
4396 let h = 1 + m;
4397 Ok(())
4398}
4399
4400fn $0fun_name() -> Result<i32, i64> {
4401 let k = foo()?;
4402 if k == 42 {
4403 return Ok(1);
4404 }
4405 let m = k + 1;
4406 Ok(m)
4407}
4408"#,
4409 );
4410 }
4411
4412 #[test]
4413 fn param_usage_in_macro() {
4414 check_assist(
4415 extract_function,
4416 r#"
4417macro_rules! m {
4418 ($val:expr) => { $val };
4419}
4420
4421fn foo() {
4422 let n = 1;
4423 $0let k = n * m!(n);$0
4424 let m = k + 1;
4425}
4426"#,
4427 r#"
4428macro_rules! m {
4429 ($val:expr) => { $val };
4430}
4431
4432fn foo() {
4433 let n = 1;
4434 let k = fun_name(n);
4435 let m = k + 1;
4436}
4437
4438fn $0fun_name(n: i32) -> i32 {
4439 let k = n * m!(n);
4440 k
4441}
4442"#,
4443 );
4444 }
4445
4446 #[test]
4447 fn param_usage_in_macro_with_nested_tt() {
4448 check_assist(
4449 extract_function,
4450 r#"
4451macro_rules! m {
4452 ($val:expr) => { $val };
4453}
4454
4455fn foo() {
4456 let n = 1;
4457 let t = 1;
4458 $0let k = n * m!((n) + { t });$0
4459 let m = k + 1;
4460}
4461"#,
4462 r#"
4463macro_rules! m {
4464 ($val:expr) => { $val };
4465}
4466
4467fn foo() {
4468 let n = 1;
4469 let t = 1;
4470 let k = fun_name(n, t);
4471 let m = k + 1;
4472}
4473
4474fn $0fun_name(n: i32, t: i32) -> i32 {
4475 let k = n * m!((n) + { t });
4476 k
4477}
4478"#,
4479 )
4480 }
4481
4482 #[test]
4483 fn param_usage_in_macro_with_nested_tt_2() {
4484 check_assist(
4485 extract_function,
4486 r#"
4487macro_rules! m {
4488 ($val:expr) => { $val };
4489}
4490
4491struct S(i32);
4492impl S {
4493 fn foo(&self) {
4494 let n = 1;
4495 $0let k = n * m!((n) + { self.0 });$0
4496 let m = k + 1;
4497 }
4498}
4499"#,
4500 r#"
4501macro_rules! m {
4502 ($val:expr) => { $val };
4503}
4504
4505struct S(i32);
4506impl S {
4507 fn foo(&self) {
4508 let n = 1;
4509 let k = self.fun_name(n);
4510 let m = k + 1;
4511 }
4512
4513 fn $0fun_name(&self, n: i32) -> i32 {
4514 let k = n * m!((n) + { self.0 });
4515 k
4516 }
4517}
4518"#,
4519 )
4520 }
4521
4522 #[test]
4523 fn extract_with_await() {
4524 check_assist(
4525 extract_function,
4526 r#"
4527//- minicore: future
4528fn main() {
4529 $0some_function().await;$0
4530}
4531
4532async fn some_function() {
4533
4534}
4535"#,
4536 r#"
4537fn main() {
4538 fun_name().await;
4539}
4540
4541async fn $0fun_name() {
4542 some_function().await;
4543}
4544
4545async fn some_function() {
4546
4547}
4548"#,
4549 );
4550 }
4551
4552 #[test]
4553 fn extract_with_await_and_result_not_producing_match_expr() {
4554 check_assist(
4555 extract_function,
4556 r#"
4557//- minicore: future, result
4558async fn foo() -> Result<(), ()> {
4559 $0async {}.await;
4560 Err(())?$0
4561}
4562"#,
4563 r#"
4564async fn foo() -> Result<(), ()> {
4565 fun_name().await
4566}
4567
4568async fn $0fun_name() -> Result<(), ()> {
4569 async {}.await;
4570 Err(())?
4571}
4572"#,
4573 );
4574 }
4575
4576 #[test]
4577 fn extract_with_await_and_result_producing_match_expr() {
4578 check_assist(
4579 extract_function,
4580 r#"
4581//- minicore: future
4582async fn foo() -> i32 {
4583 loop {
4584 let n = 1;$0
4585 let k = async { 1 }.await;
4586 if k == 42 {
4587 break 3;
4588 }
4589 let m = k + 1;$0
4590 let h = 1 + m;
4591 }
4592}
4593"#,
4594 r#"
4595async fn foo() -> i32 {
4596 loop {
4597 let n = 1;
4598 let m = match fun_name().await {
4599 Ok(value) => value,
4600 Err(value) => break value,
4601 };
4602 let h = 1 + m;
4603 }
4604}
4605
4606async fn $0fun_name() -> Result<i32, i32> {
4607 let k = async { 1 }.await;
4608 if k == 42 {
4609 return Err(3);
4610 }
4611 let m = k + 1;
4612 Ok(m)
4613}
4614"#,
4615 );
4616 }
4617
4618 #[test]
4619 fn extract_with_await_in_args() {
4620 check_assist(
4621 extract_function,
4622 r#"
4623//- minicore: future
4624fn main() {
4625 $0function_call("a", some_function().await);$0
4626}
4627
4628async fn some_function() {
4629
4630}
4631"#,
4632 r#"
4633fn main() {
4634 fun_name().await;
4635}
4636
4637async fn $0fun_name() {
4638 function_call("a", some_function().await);
4639}
4640
4641async fn some_function() {
4642
4643}
4644"#,
4645 );
4646 }
4647
4648 #[test]
4649 fn extract_does_not_extract_standalone_blocks() {
4650 check_assist_not_applicable(
4651 extract_function,
4652 r#"
4653fn main() $0{}$0
4654"#,
4655 );
4656 }
4657
4658 #[test]
4659 fn extract_adds_comma_for_match_arm() {
4660 check_assist(
4661 extract_function,
4662 r#"
4663fn main() {
4664 match 6 {
4665 100 => $0{ 100 }$0
4666 _ => 0,
4667 };
4668}
4669"#,
4670 r#"
4671fn main() {
4672 match 6 {
4673 100 => fun_name(),
4674 _ => 0,
4675 };
4676}
4677
4678fn $0fun_name() -> i32 {
4679 100
4680}
4681"#,
4682 );
4683 check_assist(
4684 extract_function,
4685 r#"
4686fn main() {
4687 match 6 {
4688 100 => $0{ 100 }$0,
4689 _ => 0,
4690 };
4691}
4692"#,
4693 r#"
4694fn main() {
4695 match 6 {
4696 100 => fun_name(),
4697 _ => 0,
4698 };
4699}
4700
4701fn $0fun_name() -> i32 {
4702 100
4703}
4704"#,
4705 );
4706
4707 check_assist(
4709 extract_function,
4710 r#"
4711fn main() {
4712 match () {
4713 _ => $0()$0,
4714 }
4715}
4716"#,
4717 r#"
4718fn main() {
4719 match () {
4720 _ => fun_name(),
4721 }
4722}
4723
4724fn $0fun_name() {
4725 ()
4726}
4727"#,
4728 )
4729 }
4730
4731 #[test]
4732 fn extract_does_not_tear_comments_apart() {
4733 check_assist(
4734 extract_function,
4735 r#"
4736fn foo() {
4737 /*$0*/
4738 foo();
4739 foo();
4740 /*$0*/
4741}
4742"#,
4743 r#"
4744fn foo() {
4745 fun_name();
4746}
4747
4748fn $0fun_name() {
4749 /**/
4750 foo();
4751 foo();
4752 /**/
4753}
4754"#,
4755 );
4756 }
4757
4758 #[test]
4759 fn extract_does_not_tear_body_apart() {
4760 check_assist(
4761 extract_function,
4762 r#"
4763fn foo() {
4764 $0foo();
4765}$0
4766"#,
4767 r#"
4768fn foo() {
4769 fun_name();
4770}
4771
4772fn $0fun_name() {
4773 foo();
4774}
4775"#,
4776 );
4777 }
4778
4779 #[test]
4780 fn extract_does_not_wrap_res_in_res() {
4781 check_assist(
4782 extract_function,
4783 r#"
4784//- minicore: result, try
4785fn foo() -> Result<(), i64> {
4786 $0Result::<i32, i64>::Ok(0)?;
4787 Ok(())$0
4788}
4789"#,
4790 r#"
4791fn foo() -> Result<(), i64> {
4792 fun_name()
4793}
4794
4795fn $0fun_name() -> Result<(), i64> {
4796 Result::<i32, i64>::Ok(0)?;
4797 Ok(())
4798}
4799"#,
4800 );
4801 }
4802
4803 #[test]
4804 fn extract_knows_const() {
4805 check_assist(
4806 extract_function,
4807 r#"
4808const fn foo() {
4809 $0()$0
4810}
4811"#,
4812 r#"
4813const fn foo() {
4814 fun_name();
4815}
4816
4817const fn $0fun_name() {
4818 ()
4819}
4820"#,
4821 );
4822 check_assist(
4823 extract_function,
4824 r#"
4825const FOO: () = {
4826 $0()$0
4827};
4828"#,
4829 r#"
4830const FOO: () = {
4831 fun_name();
4832};
4833
4834const fn $0fun_name() {
4835 ()
4836}
4837"#,
4838 );
4839 }
4840
4841 #[test]
4842 fn extract_does_not_move_outer_loop_vars() {
4843 check_assist(
4844 extract_function,
4845 r#"
4846//- minicore: iterator
4847fn foo() {
4848 let mut x = 5;
4849 for _ in 0..10 {
4850 $0x += 1;$0
4851 }
4852}
4853"#,
4854 r#"
4855fn foo() {
4856 let mut x = 5;
4857 for _ in 0..10 {
4858 fun_name(&mut x);
4859 }
4860}
4861
4862fn $0fun_name(x: &mut i32) {
4863 *x += 1;
4864}
4865"#,
4866 );
4867 check_assist(
4868 extract_function,
4869 r#"
4870//- minicore: iterator
4871fn foo() {
4872 for _ in 0..10 {
4873 let mut x = 5;
4874 $0x += 1;$0
4875 }
4876}
4877"#,
4878 r#"
4879fn foo() {
4880 for _ in 0..10 {
4881 let mut x = 5;
4882 fun_name(x);
4883 }
4884}
4885
4886fn $0fun_name(mut x: i32) {
4887 x += 1;
4888}
4889"#,
4890 );
4891 check_assist(
4892 extract_function,
4893 r#"
4894//- minicore: iterator
4895fn foo() {
4896 loop {
4897 let mut x = 5;
4898 for _ in 0..10 {
4899 $0x += 1;$0
4900 }
4901 }
4902}
4903"#,
4904 r#"
4905fn foo() {
4906 loop {
4907 let mut x = 5;
4908 for _ in 0..10 {
4909 fun_name(&mut x);
4910 }
4911 }
4912}
4913
4914fn $0fun_name(x: &mut i32) {
4915 *x += 1;
4916}
4917"#,
4918 );
4919 }
4920
4921 #[test]
4923 fn extract_mut_ref_param_has_no_mut_binding_in_loop() {
4924 check_assist(
4925 extract_function,
4926 r#"
4927struct Foo;
4928impl Foo {
4929 fn foo(&mut self) {}
4930}
4931fn foo() {
4932 let mut x = Foo;
4933 while false {
4934 let y = &mut x;
4935 $0y.foo();$0
4936 }
4937 let z = x;
4938}
4939"#,
4940 r#"
4941struct Foo;
4942impl Foo {
4943 fn foo(&mut self) {}
4944}
4945fn foo() {
4946 let mut x = Foo;
4947 while false {
4948 let y = &mut x;
4949 fun_name(y);
4950 }
4951 let z = x;
4952}
4953
4954fn $0fun_name(y: &mut Foo) {
4955 y.foo();
4956}
4957"#,
4958 );
4959 }
4960
4961 #[test]
4962 fn extract_with_macro_arg() {
4963 check_assist(
4964 extract_function,
4965 r#"
4966macro_rules! m {
4967 ($val:expr) => { $val };
4968}
4969fn main() {
4970 let bar = "bar";
4971 $0m!(bar);$0
4972}
4973"#,
4974 r#"
4975macro_rules! m {
4976 ($val:expr) => { $val };
4977}
4978fn main() {
4979 let bar = "bar";
4980 fun_name(bar);
4981}
4982
4983fn $0fun_name(bar: &str) {
4984 m!(bar);
4985}
4986"#,
4987 );
4988 }
4989
4990 #[test]
4991 fn unresolvable_types_default_to_placeholder() {
4992 check_assist(
4993 extract_function,
4994 r#"
4995fn foo() {
4996 let a = __unresolved;
4997 let _ = $0{a}$0;
4998}
4999"#,
5000 r#"
5001fn foo() {
5002 let a = __unresolved;
5003 let _ = fun_name(a);
5004}
5005
5006fn $0fun_name(a: _) -> _ {
5007 a
5008}
5009"#,
5010 );
5011 }
5012
5013 #[test]
5014 fn reference_mutable_param_with_further_usages() {
5015 check_assist(
5016 extract_function,
5017 r#"
5018pub struct Foo {
5019 field: u32,
5020}
5021
5022pub fn testfn(arg: &mut Foo) {
5023 $0arg.field = 8;$0
5024 // Simulating access after the extracted portion
5025 arg.field = 16;
5026}
5027"#,
5028 r#"
5029pub struct Foo {
5030 field: u32,
5031}
5032
5033pub fn testfn(arg: &mut Foo) {
5034 fun_name(arg);
5035 // Simulating access after the extracted portion
5036 arg.field = 16;
5037}
5038
5039fn $0fun_name(arg: &mut Foo) {
5040 arg.field = 8;
5041}
5042"#,
5043 );
5044 }
5045
5046 #[test]
5047 fn reference_mutable_param_without_further_usages() {
5048 check_assist(
5049 extract_function,
5050 r#"
5051pub struct Foo {
5052 field: u32,
5053}
5054
5055pub fn testfn(arg: &mut Foo) {
5056 $0arg.field = 8;$0
5057}
5058"#,
5059 r#"
5060pub struct Foo {
5061 field: u32,
5062}
5063
5064pub fn testfn(arg: &mut Foo) {
5065 fun_name(arg);
5066}
5067
5068fn $0fun_name(arg: &mut Foo) {
5069 arg.field = 8;
5070}
5071"#,
5072 );
5073 }
5074 #[test]
5075 fn does_not_import_control_flow() {
5076 check_assist(
5077 extract_function,
5078 r#"
5079//- minicore: try
5080fn func() {
5081 $0let cf = "I'm ControlFlow";$0
5082}
5083"#,
5084 r#"
5085fn func() {
5086 fun_name();
5087}
5088
5089fn $0fun_name() {
5090 let cf = "I'm ControlFlow";
5091}
5092"#,
5093 );
5094 }
5095
5096 #[test]
5097 fn extract_function_copies_comment_at_start() {
5098 check_assist(
5099 extract_function,
5100 r#"
5101fn func() {
5102 let i = 0;
5103 $0// comment here!
5104 let x = 0;$0
5105}
5106"#,
5107 r#"
5108fn func() {
5109 let i = 0;
5110 fun_name();
5111}
5112
5113fn $0fun_name() {
5114 // comment here!
5115 let x = 0;
5116}
5117"#,
5118 );
5119 }
5120
5121 #[test]
5122 fn extract_function_copies_comment_in_between() {
5123 check_assist(
5124 extract_function,
5125 r#"
5126fn func() {
5127 let i = 0;$0
5128 let a = 0;
5129 // comment here!
5130 let x = 0;$0
5131}
5132"#,
5133 r#"
5134fn func() {
5135 let i = 0;
5136 fun_name();
5137}
5138
5139fn $0fun_name() {
5140 let a = 0;
5141 // comment here!
5142 let x = 0;
5143}
5144"#,
5145 );
5146 }
5147
5148 #[test]
5149 fn extract_function_copies_comment_at_end() {
5150 check_assist(
5151 extract_function,
5152 r#"
5153fn func() {
5154 let i = 0;
5155 $0let x = 0;
5156 // comment here!$0
5157}
5158"#,
5159 r#"
5160fn func() {
5161 let i = 0;
5162 fun_name();
5163}
5164
5165fn $0fun_name() {
5166 let x = 0;
5167 // comment here!
5168}
5169"#,
5170 );
5171 }
5172
5173 #[test]
5174 fn extract_function_copies_comment_indented() {
5175 check_assist(
5176 extract_function,
5177 r#"
5178fn func() {
5179 let i = 0;
5180 $0let x = 0;
5181 while(true) {
5182 // comment here!
5183 }$0
5184}
5185"#,
5186 r#"
5187fn func() {
5188 let i = 0;
5189 fun_name();
5190}
5191
5192fn $0fun_name() {
5193 let x = 0;
5194 while(true) {
5195 // comment here!
5196 }
5197}
5198"#,
5199 );
5200 }
5201
5202 #[test]
5203 fn extract_function_does_preserve_whitespace() {
5204 check_assist(
5205 extract_function,
5206 r#"
5207fn func() {
5208 let i = 0;
5209 $0let a = 0;
5210
5211 let x = 0;$0
5212}
5213"#,
5214 r#"
5215fn func() {
5216 let i = 0;
5217 fun_name();
5218}
5219
5220fn $0fun_name() {
5221 let a = 0;
5222
5223 let x = 0;
5224}
5225"#,
5226 );
5227 }
5228
5229 #[test]
5230 fn extract_function_long_form_comment() {
5231 check_assist(
5232 extract_function,
5233 r#"
5234fn func() {
5235 let i = 0;
5236 $0/* a comment */
5237 let x = 0;$0
5238}
5239"#,
5240 r#"
5241fn func() {
5242 let i = 0;
5243 fun_name();
5244}
5245
5246fn $0fun_name() {
5247 /* a comment */
5248 let x = 0;
5249}
5250"#,
5251 );
5252 }
5253
5254 #[test]
5255 fn it_should_not_generate_duplicate_function_names() {
5256 check_assist(
5257 extract_function,
5258 r#"
5259fn fun_name() {
5260 $0let x = 0;$0
5261}
5262"#,
5263 r#"
5264fn fun_name() {
5265 fun_name1();
5266}
5267
5268fn $0fun_name1() {
5269 let x = 0;
5270}
5271"#,
5272 );
5273 }
5274
5275 #[test]
5276 fn should_increment_suffix_until_it_finds_space() {
5277 check_assist(
5278 extract_function,
5279 r#"
5280fn fun_name1() {
5281 let y = 0;
5282}
5283
5284fn fun_name() {
5285 $0let x = 0;$0
5286}
5287"#,
5288 r#"
5289fn fun_name1() {
5290 let y = 0;
5291}
5292
5293fn fun_name() {
5294 fun_name2();
5295}
5296
5297fn $0fun_name2() {
5298 let x = 0;
5299}
5300"#,
5301 );
5302 }
5303
5304 #[test]
5305 fn extract_method_from_trait_impl() {
5306 check_assist(
5307 extract_function,
5308 r#"
5309struct Struct(i32);
5310trait Trait {
5311 fn bar(&self) -> i32;
5312}
5313
5314impl Trait for Struct {
5315 fn bar(&self) -> i32 {
5316 $0self.0 + 2$0
5317 }
5318}
5319"#,
5320 r#"
5321struct Struct(i32);
5322trait Trait {
5323 fn bar(&self) -> i32;
5324}
5325
5326impl Trait for Struct {
5327 fn bar(&self) -> i32 {
5328 self.fun_name()
5329 }
5330}
5331
5332impl Struct {
5333 fn $0fun_name(&self) -> i32 {
5334 self.0 + 2
5335 }
5336}
5337"#,
5338 );
5339 }
5340
5341 #[test]
5342 fn extract_method_from_trait_with_existing_non_empty_impl_block() {
5343 check_assist(
5344 extract_function,
5345 r#"
5346struct Struct(i32);
5347trait Trait {
5348 fn bar(&self) -> i32;
5349}
5350
5351impl Struct {
5352 fn foo() {}
5353}
5354
5355impl Trait for Struct {
5356 fn bar(&self) -> i32 {
5357 $0self.0 + 2$0
5358 }
5359}
5360"#,
5361 r#"
5362struct Struct(i32);
5363trait Trait {
5364 fn bar(&self) -> i32;
5365}
5366
5367impl Struct {
5368 fn foo() {}
5369
5370 fn $0fun_name(&self) -> i32 {
5371 self.0 + 2
5372 }
5373}
5374
5375impl Trait for Struct {
5376 fn bar(&self) -> i32 {
5377 self.fun_name()
5378 }
5379}
5380"#,
5381 )
5382 }
5383
5384 #[test]
5385 fn extract_function_from_trait_with_existing_non_empty_impl_block() {
5386 check_assist(
5387 extract_function,
5388 r#"
5389struct Struct(i32);
5390trait Trait {
5391 fn bar(&self) -> i32;
5392}
5393
5394impl Struct {
5395 fn foo() {}
5396}
5397
5398impl Trait for Struct {
5399 fn bar(&self) -> i32 {
5400 let three_squared = $03 * 3$0;
5401 self.0 + three_squared
5402 }
5403}
5404"#,
5405 r#"
5406struct Struct(i32);
5407trait Trait {
5408 fn bar(&self) -> i32;
5409}
5410
5411impl Struct {
5412 fn foo() {}
5413}
5414
5415impl Trait for Struct {
5416 fn bar(&self) -> i32 {
5417 let three_squared = fun_name();
5418 self.0 + three_squared
5419 }
5420}
5421
5422fn $0fun_name() -> i32 {
5423 3 * 3
5424}
5425"#,
5426 )
5427 }
5428
5429 #[test]
5430 fn extract_method_from_trait_with_multiple_existing_impl_blocks() {
5431 check_assist(
5432 extract_function,
5433 r#"
5434struct Struct(i32);
5435struct StructBefore(i32);
5436struct StructAfter(i32);
5437trait Trait {
5438 fn bar(&self) -> i32;
5439}
5440
5441impl StructBefore {
5442 fn foo(){}
5443}
5444
5445impl Struct {
5446 fn foo(){}
5447}
5448
5449impl StructAfter {
5450 fn foo(){}
5451}
5452
5453impl Trait for Struct {
5454 fn bar(&self) -> i32 {
5455 $0self.0 + 2$0
5456 }
5457}
5458"#,
5459 r#"
5460struct Struct(i32);
5461struct StructBefore(i32);
5462struct StructAfter(i32);
5463trait Trait {
5464 fn bar(&self) -> i32;
5465}
5466
5467impl StructBefore {
5468 fn foo(){}
5469}
5470
5471impl Struct {
5472 fn foo(){}
5473
5474 fn $0fun_name(&self) -> i32 {
5475 self.0 + 2
5476 }
5477}
5478
5479impl StructAfter {
5480 fn foo(){}
5481}
5482
5483impl Trait for Struct {
5484 fn bar(&self) -> i32 {
5485 self.fun_name()
5486 }
5487}
5488"#,
5489 )
5490 }
5491
5492 #[test]
5493 fn extract_method_from_trait_with_multiple_existing_trait_impl_blocks() {
5494 check_assist(
5495 extract_function,
5496 r#"
5497struct Struct(i32);
5498trait Trait {
5499 fn bar(&self) -> i32;
5500}
5501trait TraitBefore {
5502 fn before(&self) -> i32;
5503}
5504trait TraitAfter {
5505 fn after(&self) -> i32;
5506}
5507
5508impl TraitBefore for Struct {
5509 fn before(&self) -> i32 {
5510 42
5511 }
5512}
5513
5514impl Struct {
5515 fn foo(){}
5516}
5517
5518impl TraitAfter for Struct {
5519 fn after(&self) -> i32 {
5520 42
5521 }
5522}
5523
5524impl Trait for Struct {
5525 fn bar(&self) -> i32 {
5526 $0self.0 + 2$0
5527 }
5528}
5529"#,
5530 r#"
5531struct Struct(i32);
5532trait Trait {
5533 fn bar(&self) -> i32;
5534}
5535trait TraitBefore {
5536 fn before(&self) -> i32;
5537}
5538trait TraitAfter {
5539 fn after(&self) -> i32;
5540}
5541
5542impl TraitBefore for Struct {
5543 fn before(&self) -> i32 {
5544 42
5545 }
5546}
5547
5548impl Struct {
5549 fn foo(){}
5550
5551 fn $0fun_name(&self) -> i32 {
5552 self.0 + 2
5553 }
5554}
5555
5556impl TraitAfter for Struct {
5557 fn after(&self) -> i32 {
5558 42
5559 }
5560}
5561
5562impl Trait for Struct {
5563 fn bar(&self) -> i32 {
5564 self.fun_name()
5565 }
5566}
5567"#,
5568 )
5569 }
5570
5571 #[test]
5572 fn closure_arguments() {
5573 check_assist(
5574 extract_function,
5575 r#"
5576fn parent(factor: i32) {
5577 let v = &[1, 2, 3];
5578
5579 $0v.iter().map(|it| it * factor);$0
5580}
5581"#,
5582 r#"
5583fn parent(factor: i32) {
5584 let v = &[1, 2, 3];
5585
5586 fun_name(factor, v);
5587}
5588
5589fn $0fun_name(factor: i32, v: &[i32; 3]) {
5590 v.iter().map(|it| it * factor);
5591}
5592"#,
5593 );
5594 }
5595
5596 #[test]
5597 fn preserve_generics() {
5598 check_assist(
5599 extract_function,
5600 r#"
5601fn func<T: Debug>(i: T) {
5602 $0foo(i);$0
5603}
5604"#,
5605 r#"
5606fn func<T: Debug>(i: T) {
5607 fun_name(i);
5608}
5609
5610fn $0fun_name<T: Debug>(i: T) {
5611 foo(i);
5612}
5613"#,
5614 );
5615 }
5616
5617 #[test]
5618 fn dont_emit_type_with_hidden_lifetime_parameter() {
5619 check_assist(
5621 extract_function,
5622 r#"
5623struct Struct<'a, T>(&'a T);
5624fn func<T: Debug>(i: Struct<'_, T>) {
5625 $0foo(i);$0
5626}
5627"#,
5628 r#"
5629struct Struct<'a, T>(&'a T);
5630fn func<T: Debug>(i: Struct<'_, T>) {
5631 fun_name(i);
5632}
5633
5634fn $0fun_name(i: Struct<'_, T>) {
5635 foo(i);
5636}
5637"#,
5638 );
5639 }
5640
5641 #[test]
5642 fn preserve_generics_from_body() {
5643 check_assist(
5644 extract_function,
5645 r#"
5646fn func<T: Default>() -> T {
5647 $0T::default()$0
5648}
5649"#,
5650 r#"
5651fn func<T: Default>() -> T {
5652 fun_name()
5653}
5654
5655fn $0fun_name<T: Default>() -> T {
5656 T::default()
5657}
5658"#,
5659 );
5660 }
5661
5662 #[test]
5663 fn filter_unused_generics() {
5664 check_assist(
5665 extract_function,
5666 r#"
5667fn func<T: Debug, U: Copy>(i: T, u: U) {
5668 bar(u);
5669 $0foo(i);$0
5670}
5671"#,
5672 r#"
5673fn func<T: Debug, U: Copy>(i: T, u: U) {
5674 bar(u);
5675 fun_name(i);
5676}
5677
5678fn $0fun_name<T: Debug>(i: T) {
5679 foo(i);
5680}
5681"#,
5682 );
5683 }
5684
5685 #[test]
5686 fn empty_generic_param_list() {
5687 check_assist(
5688 extract_function,
5689 r#"
5690fn func<T: Debug>(t: T, i: u32) {
5691 bar(t);
5692 $0foo(i);$0
5693}
5694"#,
5695 r#"
5696fn func<T: Debug>(t: T, i: u32) {
5697 bar(t);
5698 fun_name(i);
5699}
5700
5701fn $0fun_name(i: u32) {
5702 foo(i);
5703}
5704"#,
5705 );
5706 }
5707
5708 #[test]
5709 fn preserve_where_clause() {
5710 check_assist(
5711 extract_function,
5712 r#"
5713fn func<T>(i: T) where T: Debug {
5714 $0foo(i);$0
5715}
5716"#,
5717 r#"
5718fn func<T>(i: T) where T: Debug {
5719 fun_name(i);
5720}
5721
5722fn $0fun_name<T>(i: T) where T: Debug {
5723 foo(i);
5724}
5725"#,
5726 );
5727 }
5728
5729 #[test]
5730 fn filter_unused_where_clause() {
5731 check_assist(
5732 extract_function,
5733 r#"
5734fn func<T, U>(i: T, u: U) where T: Debug, U: Copy {
5735 bar(u);
5736 $0foo(i);$0
5737}
5738"#,
5739 r#"
5740fn func<T, U>(i: T, u: U) where T: Debug, U: Copy {
5741 bar(u);
5742 fun_name(i);
5743}
5744
5745fn $0fun_name<T>(i: T) where T: Debug {
5746 foo(i);
5747}
5748"#,
5749 );
5750 }
5751
5752 #[test]
5753 fn nested_generics() {
5754 check_assist(
5755 extract_function,
5756 r#"
5757struct Struct<T: Into<i32>>(T);
5758impl <T: Into<i32> + Copy> Struct<T> {
5759 fn func<V: Into<i32>>(&self, v: V) -> i32 {
5760 let t = self.0;
5761 $0t.into() + v.into()$0
5762 }
5763}
5764"#,
5765 r#"
5766struct Struct<T: Into<i32>>(T);
5767impl <T: Into<i32> + Copy> Struct<T> {
5768 fn func<V: Into<i32>>(&self, v: V) -> i32 {
5769 let t = self.0;
5770 fun_name(v, t)
5771 }
5772}
5773
5774fn $0fun_name<T: Into<i32> + Copy, V: Into<i32>>(v: V, t: T) -> i32 {
5775 t.into() + v.into()
5776}
5777"#,
5778 );
5779 }
5780
5781 #[test]
5782 fn filters_unused_nested_generics() {
5783 check_assist(
5784 extract_function,
5785 r#"
5786struct Struct<T: Into<i32>, U: Debug>(T, U);
5787impl <T: Into<i32> + Copy, U: Debug> Struct<T, U> {
5788 fn func<V: Into<i32>>(&self, v: V) -> i32 {
5789 let t = self.0;
5790 $0t.into() + v.into()$0
5791 }
5792}
5793"#,
5794 r#"
5795struct Struct<T: Into<i32>, U: Debug>(T, U);
5796impl <T: Into<i32> + Copy, U: Debug> Struct<T, U> {
5797 fn func<V: Into<i32>>(&self, v: V) -> i32 {
5798 let t = self.0;
5799 fun_name(v, t)
5800 }
5801}
5802
5803fn $0fun_name<T: Into<i32> + Copy, V: Into<i32>>(v: V, t: T) -> i32 {
5804 t.into() + v.into()
5805}
5806"#,
5807 );
5808 }
5809
5810 #[test]
5811 fn nested_where_clauses() {
5812 check_assist(
5813 extract_function,
5814 r#"
5815struct Struct<T>(T) where T: Into<i32>;
5816impl <T> Struct<T> where T: Into<i32> + Copy {
5817 fn func<V>(&self, v: V) -> i32 where V: Into<i32> {
5818 let t = self.0;
5819 $0t.into() + v.into()$0
5820 }
5821}
5822"#,
5823 r#"
5824struct Struct<T>(T) where T: Into<i32>;
5825impl <T> Struct<T> where T: Into<i32> + Copy {
5826 fn func<V>(&self, v: V) -> i32 where V: Into<i32> {
5827 let t = self.0;
5828 fun_name(v, t)
5829 }
5830}
5831
5832fn $0fun_name<T, V>(v: V, t: T) -> i32 where T: Into<i32> + Copy, V: Into<i32> {
5833 t.into() + v.into()
5834}
5835"#,
5836 );
5837 }
5838
5839 #[test]
5840 fn filters_unused_nested_where_clauses() {
5841 check_assist(
5842 extract_function,
5843 r#"
5844struct Struct<T, U>(T, U) where T: Into<i32>, U: Debug;
5845impl <T, U> Struct<T, U> where T: Into<i32> + Copy, U: Debug {
5846 fn func<V>(&self, v: V) -> i32 where V: Into<i32> {
5847 let t = self.0;
5848 $0t.into() + v.into()$0
5849 }
5850}
5851"#,
5852 r#"
5853struct Struct<T, U>(T, U) where T: Into<i32>, U: Debug;
5854impl <T, U> Struct<T, U> where T: Into<i32> + Copy, U: Debug {
5855 fn func<V>(&self, v: V) -> i32 where V: Into<i32> {
5856 let t = self.0;
5857 fun_name(v, t)
5858 }
5859}
5860
5861fn $0fun_name<T, V>(v: V, t: T) -> i32 where T: Into<i32> + Copy, V: Into<i32> {
5862 t.into() + v.into()
5863}
5864"#,
5865 );
5866 }
5867
5868 #[test]
5869 fn tail_expr_no_extra_control_flow() {
5870 check_assist(
5871 extract_function,
5872 r#"
5873//- minicore: result
5874fn fallible() -> Result<(), ()> {
5875 $0if true {
5876 return Err(());
5877 }
5878 Ok(())$0
5879}
5880"#,
5881 r#"
5882fn fallible() -> Result<(), ()> {
5883 fun_name()
5884}
5885
5886fn $0fun_name() -> Result<(), ()> {
5887 if true {
5888 return Err(());
5889 }
5890 Ok(())
5891}
5892"#,
5893 );
5894 }
5895
5896 #[test]
5897 fn non_tail_expr_of_tail_expr_loop() {
5898 check_assist(
5899 extract_function,
5900 r#"
5901pub fn f() {
5902 loop {
5903 $0if true {
5904 continue;
5905 }$0
5906
5907 if false {
5908 break;
5909 }
5910 }
5911}
5912"#,
5913 r#"
5914pub fn f() {
5915 loop {
5916 if let ControlFlow::Break(_) = fun_name() {
5917 continue;
5918 }
5919
5920 if false {
5921 break;
5922 }
5923 }
5924}
5925
5926fn $0fun_name() -> ControlFlow<()> {
5927 if true {
5928 return ControlFlow::Break(());
5929 }
5930 ControlFlow::Continue(())
5931}
5932"#,
5933 );
5934 }
5935
5936 #[test]
5937 fn non_tail_expr_of_tail_if_block() {
5938 check_assist(
5940 extract_function,
5941 r#"
5942//- minicore: option, try
5943fn f() -> Option<()> {
5944 if true {
5945 let a = $0if true {
5946 Some(())?
5947 } else {
5948 ()
5949 }$0;
5950 Some(a)
5951 } else {
5952 None
5953 }
5954}
5955"#,
5956 r#"
5957fn f() -> Option<()> {
5958 if true {
5959 let a = fun_name()?;;
5960 Some(a)
5961 } else {
5962 None
5963 }
5964}
5965
5966fn $0fun_name() -> Option<()> {
5967 Some(if true {
5968 Some(())?
5969 } else {
5970 ()
5971 })
5972}
5973"#,
5974 );
5975 }
5976
5977 #[test]
5978 fn tail_expr_of_tail_block_nested() {
5979 check_assist(
5980 extract_function,
5981 r#"
5982//- minicore: option, try
5983fn f() -> Option<()> {
5984 if true {
5985 $0{
5986 let a = if true {
5987 Some(())?
5988 } else {
5989 ()
5990 };
5991 Some(a)
5992 }$0
5993 } else {
5994 None
5995 }
5996}
5997"#,
5998 r#"
5999fn f() -> Option<()> {
6000 if true {
6001 fun_name()
6002 } else {
6003 None
6004 }
6005}
6006
6007fn $0fun_name() -> Option<()> {
6008 let a = if true {
6009 Some(())?
6010 } else {
6011 ()
6012 };
6013 Some(a)
6014}
6015"#,
6016 );
6017 }
6018
6019 #[test]
6020 fn non_tail_expr_with_comment_of_tail_expr_loop() {
6021 check_assist(
6022 extract_function,
6023 r#"
6024pub fn f() {
6025 loop {
6026 $0// A comment
6027 if true {
6028 continue;
6029 }$0
6030 if false {
6031 break;
6032 }
6033 }
6034}
6035"#,
6036 r#"
6037pub fn f() {
6038 loop {
6039 if let ControlFlow::Break(_) = fun_name() {
6040 continue;
6041 }
6042 if false {
6043 break;
6044 }
6045 }
6046}
6047
6048fn $0fun_name() -> ControlFlow<()> {
6049 // A comment
6050 if true {
6051 return ControlFlow::Break(());
6052 }
6053 ControlFlow::Continue(())
6054}
6055"#,
6056 );
6057 }
6058
6059 #[test]
6060 fn comments_in_block_expr() {
6061 check_assist(
6062 extract_function,
6063 r#"
6064fn f() {
6065 let c = $0{
6066 // comment 1
6067 let a = 2 + 3;
6068 // comment 2
6069 let b = 5;
6070 a + b
6071 }$0;
6072}
6073"#,
6074 r#"
6075fn f() {
6076 let c = fun_name();
6077}
6078
6079fn $0fun_name() -> i32 {
6080 // comment 1
6081 let a = 2 + 3;
6082 // comment 2
6083 let b = 5;
6084 a + b
6085}
6086"#,
6087 );
6088 }
6089
6090 #[test]
6091 fn sort_params_in_order() {
6092 check_assist(
6093 extract_function,
6094 r#"
6095fn existing(a: i32, b: i32, c: i32) {
6096 let x = 32;
6097
6098 let p = $0x + b + c + a$0;
6099}
6100"#,
6101 r#"
6102fn existing(a: i32, b: i32, c: i32) {
6103 let x = 32;
6104
6105 let p = fun_name(a, b, c, x);
6106}
6107
6108fn $0fun_name(a: i32, b: i32, c: i32, x: i32) -> i32 {
6109 x + b + c + a
6110}
6111"#,
6112 );
6113 }
6114
6115 #[test]
6116 fn fmt_macro_argument() {
6117 check_assist(
6118 extract_function,
6119 r#"
6120//- minicore: fmt
6121fn existing(a: i32, b: i32, c: i32) {
6122 $0print!("{a}{}{}", b, "{c}");$0
6123}
6124"#,
6125 r#"
6126fn existing(a: i32, b: i32, c: i32) {
6127 fun_name(a, b);
6128}
6129
6130fn $0fun_name(a: i32, b: i32) {
6131 print!("{a}{}{}", b, "{c}");
6132}
6133"#,
6134 );
6135 }
6136
6137 #[test]
6138 fn in_left_curly_is_not_applicable() {
6139 cov_mark::check!(extract_function_in_braces_is_not_applicable);
6140 check_assist_not_applicable(extract_function, r"fn foo() { $0}$0");
6141 }
6142
6143 #[test]
6144 fn in_right_curly_is_not_applicable() {
6145 cov_mark::check!(extract_function_in_braces_is_not_applicable);
6146 check_assist_not_applicable(extract_function, r"fn foo() $0{$0 }");
6147 }
6148
6149 #[test]
6150 fn in_left_paren_is_not_applicable() {
6151 cov_mark::check!(extract_function_in_braces_is_not_applicable);
6152 check_assist_not_applicable(extract_function, r"fn foo( $0)$0 { }");
6153 }
6154
6155 #[test]
6156 fn in_right_paren_is_not_applicable() {
6157 cov_mark::check!(extract_function_in_braces_is_not_applicable);
6158 check_assist_not_applicable(extract_function, r"fn foo $0($0 ) { }");
6159 }
6160
6161 #[test]
6162 fn in_left_brack_is_not_applicable() {
6163 cov_mark::check!(extract_function_in_braces_is_not_applicable);
6164 check_assist_not_applicable(extract_function, r"fn foo(arr: &mut [i32$0]$0) {}");
6165 }
6166
6167 #[test]
6168 fn in_right_brack_is_not_applicable() {
6169 cov_mark::check!(extract_function_in_braces_is_not_applicable);
6170 check_assist_not_applicable(extract_function, r"fn foo(arr: &mut $0[$0i32]) {}");
6171 }
6172
6173 #[test]
6174 fn issue_20965_panic() {
6175 check_assist(
6176 extract_function,
6177 r#"
6178//- minicore: fmt
6179#[derive(Debug)]
6180struct Foo(&'static str);
6181
6182impl Foo {
6183 fn text(&self) -> &str { self.0 }
6184}
6185
6186fn main() {
6187 let s = Foo("");
6188 $0print!("{}{}", s, s);$0
6189 let _ = s.text() == "";
6190}"#,
6191 r#"
6192#[derive(Debug)]
6193struct Foo(&'static str);
6194
6195impl Foo {
6196 fn text(&self) -> &str { self.0 }
6197}
6198
6199fn main() {
6200 let s = Foo("");
6201 fun_name(&s);
6202 let _ = s.text() == "";
6203}
6204
6205fn $0fun_name(s: &Foo) {
6206 *print!("{}{}", s, s);
6207}"#,
6208 );
6209 }
6210
6211 #[test]
6212 fn parameter_is_added_used_in_eq_expression_in_macro() {
6213 check_assist(
6214 extract_function,
6215 r#"
6216//- minicore: fmt
6217fn foo() {
6218 let v = 123;
6219 $0print!("{v:?}{}", v == 123);$0
6220}"#,
6221 r#"
6222fn foo() {
6223 let v = 123;
6224 fun_name(v);
6225}
6226
6227fn $0fun_name(v: i32) {
6228 print!("{v:?}{}", v == 123);
6229}"#,
6230 );
6231 }
6232
6233 #[test]
6234 fn no_parameter_for_variable_used_only_let_else() {
6235 check_assist(
6236 extract_function,
6237 r#"
6238fn foo() -> u32 {
6239 let x = 5;
6240
6241 $0let Some(y) = Some(1) else {
6242 return x * 2;
6243 };$0
6244
6245 y
6246}"#,
6247 r#"
6248fn foo() -> u32 {
6249 let x = 5;
6250
6251 let y = match fun_name(x) {
6252 Ok(value) => value,
6253 Err(value) => return value,
6254 };
6255
6256 y
6257}
6258
6259fn $0fun_name(x: u32) -> Result<_, u32> {
6260 let Some(y) = Some(1) else {
6261 return Err(x * 2);
6262 };
6263 Ok(y)
6264}"#,
6265 );
6266 }
6267
6268 #[test]
6269 fn deeply_nested_macros() {
6270 check_assist(
6271 extract_function,
6272 r#"
6273macro_rules! m {
6274 ($val:ident) => { $val };
6275}
6276
6277macro_rules! n {
6278 ($v1:ident, $v2:ident) => { m!($v1) + $v2 };
6279}
6280
6281macro_rules! o {
6282 ($v1:ident, $v2:ident, $v3:ident) => { n!($v1, $v2) + $v3 };
6283}
6284
6285fn foo() -> u32 {
6286 let v1 = 1;
6287 let v2 = 2;
6288 $0let v3 = 3;
6289 o!(v1, v2, v3)$0
6290}"#,
6291 r#"
6292macro_rules! m {
6293 ($val:ident) => { $val };
6294}
6295
6296macro_rules! n {
6297 ($v1:ident, $v2:ident) => { m!($v1) + $v2 };
6298}
6299
6300macro_rules! o {
6301 ($v1:ident, $v2:ident, $v3:ident) => { n!($v1, $v2) + $v3 };
6302}
6303
6304fn foo() -> u32 {
6305 let v1 = 1;
6306 let v2 = 2;
6307 fun_name(v1, v2)
6308}
6309
6310fn $0fun_name(v1: u32, v2: u32) -> u32 {
6311 let v3 = 3;
6312 o!(v1, v2, v3)
6313}"#,
6314 );
6315 }
6316
6317 #[test]
6318 fn pattern_assignment() {
6319 check_assist(
6320 extract_function,
6321 r#"
6322struct Point {x: u32, y: u32};
6323
6324fn point() -> Point {
6325 Point { x: 45, y: 50 };
6326}
6327
6328fn foo() {
6329 let mut a = 1;
6330 let mut b = 3;
6331 $0Point { x: a, y: b } = point();$0
6332}
6333"#,
6334 r#"
6335struct Point {x: u32, y: u32};
6336
6337fn point() -> Point {
6338 Point { x: 45, y: 50 };
6339}
6340
6341fn foo() {
6342 let mut a = 1;
6343 let mut b = 3;
6344 fun_name(a, b);
6345}
6346
6347fn $0fun_name(mut a: u32, mut b: u32) {
6348 Point { x: a, y: b } = point();
6349}
6350"#,
6351 );
6352 }
6353
6354 #[test]
6355 fn tuple_assignment() {
6356 check_assist(
6357 extract_function,
6358 r#"
6359fn foo() {
6360 let mut a = 3;
6361 let mut b = 4;
6362 $0(a, b) = (b, a);$0
6363}
6364"#,
6365 r#"
6366fn foo() {
6367 let mut a = 3;
6368 let mut b = 4;
6369 fun_name(a, b);
6370}
6371
6372fn $0fun_name(mut a: i32, mut b: i32) {
6373 (a, b) = (b, a);
6374}
6375"#,
6376 );
6377 }
6378}