aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_assists/src/handlers/extract_function.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide_assists/src/handlers/extract_function.rs')
-rw-r--r--crates/ide_assists/src/handlers/extract_function.rs197
1 files changed, 94 insertions, 103 deletions
diff --git a/crates/ide_assists/src/handlers/extract_function.rs b/crates/ide_assists/src/handlers/extract_function.rs
index b30652a9d..6311afc1f 100644
--- a/crates/ide_assists/src/handlers/extract_function.rs
+++ b/crates/ide_assists/src/handlers/extract_function.rs
@@ -10,18 +10,18 @@ use ide_db::{
10use itertools::Itertools; 10use itertools::Itertools;
11use stdx::format_to; 11use stdx::format_to;
12use syntax::{ 12use syntax::{
13 algo::SyntaxRewriter,
14 ast::{ 13 ast::{
15 self, 14 self,
16 edit::{AstNodeEdit, IndentLevel}, 15 edit::{AstNodeEdit, IndentLevel},
17 AstNode, 16 AstNode,
18 }, 17 },
18 ted,
19 SyntaxKind::{self, BLOCK_EXPR, BREAK_EXPR, COMMENT, PATH_EXPR, RETURN_EXPR}, 19 SyntaxKind::{self, BLOCK_EXPR, BREAK_EXPR, COMMENT, PATH_EXPR, RETURN_EXPR},
20 SyntaxNode, SyntaxToken, TextRange, TextSize, TokenAtOffset, WalkEvent, T, 20 SyntaxNode, SyntaxToken, TextRange, TextSize, TokenAtOffset, WalkEvent, T,
21}; 21};
22 22
23use crate::{ 23use crate::{
24 assist_context::{AssistContext, Assists}, 24 assist_context::{AssistContext, Assists, TreeMutator},
25 AssistId, 25 AssistId,
26}; 26};
27 27
@@ -956,10 +956,10 @@ fn format_replacement(ctx: &AssistContext, fun: &Function, indent: IndentLevel)
956 let args = fun.params.iter().map(|param| param.to_arg(ctx)); 956 let args = fun.params.iter().map(|param| param.to_arg(ctx));
957 let args = make::arg_list(args); 957 let args = make::arg_list(args);
958 let call_expr = if fun.self_param.is_some() { 958 let call_expr = if fun.self_param.is_some() {
959 let self_arg = make::expr_path(make_path_from_text("self")); 959 let self_arg = make::expr_path(make::ext::ident_path("self"));
960 make::expr_method_call(self_arg, &fun.name, args) 960 make::expr_method_call(self_arg, &fun.name, args)
961 } else { 961 } else {
962 let func = make::expr_path(make_path_from_text(&fun.name)); 962 let func = make::expr_path(make::ext::ident_path(&fun.name));
963 make::expr_call(func, args) 963 make::expr_call(func, args)
964 }; 964 };
965 965
@@ -1054,11 +1054,11 @@ impl FlowHandler {
1054 make::expr_if(condition, block, None) 1054 make::expr_if(condition, block, None)
1055 } 1055 }
1056 FlowHandler::IfOption { action } => { 1056 FlowHandler::IfOption { action } => {
1057 let path = make_path_from_text("Some"); 1057 let path = make::ext::ident_path("Some");
1058 let value_pat = make::ident_pat(make::name("value")); 1058 let value_pat = make::ident_pat(make::name("value"));
1059 let pattern = make::tuple_struct_pat(path, iter::once(value_pat.into())); 1059 let pattern = make::tuple_struct_pat(path, iter::once(value_pat.into()));
1060 let cond = make::condition(call_expr, Some(pattern.into())); 1060 let cond = make::condition(call_expr, Some(pattern.into()));
1061 let value = make::expr_path(make_path_from_text("value")); 1061 let value = make::expr_path(make::ext::ident_path("value"));
1062 let action_expr = action.make_result_handler(Some(value)); 1062 let action_expr = action.make_result_handler(Some(value));
1063 let action_stmt = make::expr_stmt(action_expr); 1063 let action_stmt = make::expr_stmt(action_expr);
1064 let then = make::block_expr(iter::once(action_stmt.into()), None); 1064 let then = make::block_expr(iter::once(action_stmt.into()), None);
@@ -1068,14 +1068,14 @@ impl FlowHandler {
1068 let some_name = "value"; 1068 let some_name = "value";
1069 1069
1070 let some_arm = { 1070 let some_arm = {
1071 let path = make_path_from_text("Some"); 1071 let path = make::ext::ident_path("Some");
1072 let value_pat = make::ident_pat(make::name(some_name)); 1072 let value_pat = make::ident_pat(make::name(some_name));
1073 let pat = make::tuple_struct_pat(path, iter::once(value_pat.into())); 1073 let pat = make::tuple_struct_pat(path, iter::once(value_pat.into()));
1074 let value = make::expr_path(make_path_from_text(some_name)); 1074 let value = make::expr_path(make::ext::ident_path(some_name));
1075 make::match_arm(iter::once(pat.into()), value) 1075 make::match_arm(iter::once(pat.into()), value)
1076 }; 1076 };
1077 let none_arm = { 1077 let none_arm = {
1078 let path = make_path_from_text("None"); 1078 let path = make::ext::ident_path("None");
1079 let pat = make::path_pat(path); 1079 let pat = make::path_pat(path);
1080 make::match_arm(iter::once(pat), none.make_result_handler(None)) 1080 make::match_arm(iter::once(pat), none.make_result_handler(None))
1081 }; 1081 };
@@ -1087,17 +1087,17 @@ impl FlowHandler {
1087 let err_name = "value"; 1087 let err_name = "value";
1088 1088
1089 let ok_arm = { 1089 let ok_arm = {
1090 let path = make_path_from_text("Ok"); 1090 let path = make::ext::ident_path("Ok");
1091 let value_pat = make::ident_pat(make::name(ok_name)); 1091 let value_pat = make::ident_pat(make::name(ok_name));
1092 let pat = make::tuple_struct_pat(path, iter::once(value_pat.into())); 1092 let pat = make::tuple_struct_pat(path, iter::once(value_pat.into()));
1093 let value = make::expr_path(make_path_from_text(ok_name)); 1093 let value = make::expr_path(make::ext::ident_path(ok_name));
1094 make::match_arm(iter::once(pat.into()), value) 1094 make::match_arm(iter::once(pat.into()), value)
1095 }; 1095 };
1096 let err_arm = { 1096 let err_arm = {
1097 let path = make_path_from_text("Err"); 1097 let path = make::ext::ident_path("Err");
1098 let value_pat = make::ident_pat(make::name(err_name)); 1098 let value_pat = make::ident_pat(make::name(err_name));
1099 let pat = make::tuple_struct_pat(path, iter::once(value_pat.into())); 1099 let pat = make::tuple_struct_pat(path, iter::once(value_pat.into()));
1100 let value = make::expr_path(make_path_from_text(err_name)); 1100 let value = make::expr_path(make::ext::ident_path(err_name));
1101 make::match_arm(iter::once(pat.into()), err.make_result_handler(Some(value))) 1101 make::match_arm(iter::once(pat.into()), err.make_result_handler(Some(value)))
1102 }; 1102 };
1103 let arms = make::match_arm_list(vec![ok_arm, err_arm]); 1103 let arms = make::match_arm_list(vec![ok_arm, err_arm]);
@@ -1107,13 +1107,9 @@ impl FlowHandler {
1107 } 1107 }
1108} 1108}
1109 1109
1110fn make_path_from_text(text: &str) -> ast::Path {
1111 make::path_unqualified(make::path_segment(make::name_ref(text)))
1112}
1113
1114fn path_expr_from_local(ctx: &AssistContext, var: Local) -> ast::Expr { 1110fn path_expr_from_local(ctx: &AssistContext, var: Local) -> ast::Expr {
1115 let name = var.name(ctx.db()).unwrap().to_string(); 1111 let name = var.name(ctx.db()).unwrap().to_string();
1116 make::expr_path(make_path_from_text(&name)) 1112 make::expr_path(make::ext::ident_path(&name))
1117} 1113}
1118 1114
1119fn format_function( 1115fn format_function(
@@ -1179,7 +1175,7 @@ fn make_ret_ty(ctx: &AssistContext, module: hir::Module, fun: &Function) -> Opti
1179 fun_ty.make_ty(ctx, module) 1175 fun_ty.make_ty(ctx, module)
1180 } 1176 }
1181 FlowHandler::Try { kind: TryKind::Option } => { 1177 FlowHandler::Try { kind: TryKind::Option } => {
1182 make::ty_generic(make::name_ref("Option"), iter::once(fun_ty.make_ty(ctx, module))) 1178 make::ext::ty_option(fun_ty.make_ty(ctx, module))
1183 } 1179 }
1184 FlowHandler::Try { kind: TryKind::Result { ty: parent_ret_ty } } => { 1180 FlowHandler::Try { kind: TryKind::Result { ty: parent_ret_ty } } => {
1185 let handler_ty = parent_ret_ty 1181 let handler_ty = parent_ret_ty
@@ -1187,29 +1183,21 @@ fn make_ret_ty(ctx: &AssistContext, module: hir::Module, fun: &Function) -> Opti
1187 .nth(1) 1183 .nth(1)
1188 .map(|ty| make_ty(&ty, ctx, module)) 1184 .map(|ty| make_ty(&ty, ctx, module))
1189 .unwrap_or_else(make::ty_unit); 1185 .unwrap_or_else(make::ty_unit);
1190 make::ty_generic( 1186 make::ext::ty_result(fun_ty.make_ty(ctx, module), handler_ty)
1191 make::name_ref("Result"),
1192 vec![fun_ty.make_ty(ctx, module), handler_ty],
1193 )
1194 } 1187 }
1195 FlowHandler::If { .. } => make::ty("bool"), 1188 FlowHandler::If { .. } => make::ext::ty_bool(),
1196 FlowHandler::IfOption { action } => { 1189 FlowHandler::IfOption { action } => {
1197 let handler_ty = action 1190 let handler_ty = action
1198 .expr_ty(ctx) 1191 .expr_ty(ctx)
1199 .map(|ty| make_ty(&ty, ctx, module)) 1192 .map(|ty| make_ty(&ty, ctx, module))
1200 .unwrap_or_else(make::ty_unit); 1193 .unwrap_or_else(make::ty_unit);
1201 make::ty_generic(make::name_ref("Option"), iter::once(handler_ty)) 1194 make::ext::ty_option(handler_ty)
1202 }
1203 FlowHandler::MatchOption { .. } => {
1204 make::ty_generic(make::name_ref("Option"), iter::once(fun_ty.make_ty(ctx, module)))
1205 } 1195 }
1196 FlowHandler::MatchOption { .. } => make::ext::ty_option(fun_ty.make_ty(ctx, module)),
1206 FlowHandler::MatchResult { err } => { 1197 FlowHandler::MatchResult { err } => {
1207 let handler_ty = 1198 let handler_ty =
1208 err.expr_ty(ctx).map(|ty| make_ty(&ty, ctx, module)).unwrap_or_else(make::ty_unit); 1199 err.expr_ty(ctx).map(|ty| make_ty(&ty, ctx, module)).unwrap_or_else(make::ty_unit);
1209 make::ty_generic( 1200 make::ext::ty_result(fun_ty.make_ty(ctx, module), handler_ty)
1210 make::name_ref("Result"),
1211 vec![fun_ty.make_ty(ctx, module), handler_ty],
1212 )
1213 } 1201 }
1214 }; 1202 };
1215 Some(make::ret_type(ret_ty)) 1203 Some(make::ret_type(ret_ty))
@@ -1296,7 +1284,7 @@ fn make_body(
1296 TryKind::Option => "Some", 1284 TryKind::Option => "Some",
1297 TryKind::Result { .. } => "Ok", 1285 TryKind::Result { .. } => "Ok",
1298 }; 1286 };
1299 let func = make::expr_path(make_path_from_text(constructor)); 1287 let func = make::expr_path(make::ext::ident_path(constructor));
1300 let args = make::arg_list(iter::once(tail_expr)); 1288 let args = make::arg_list(iter::once(tail_expr));
1301 make::expr_call(func, args) 1289 make::expr_call(func, args)
1302 }) 1290 })
@@ -1306,16 +1294,16 @@ fn make_body(
1306 with_tail_expr(block, lit_false.into()) 1294 with_tail_expr(block, lit_false.into())
1307 } 1295 }
1308 FlowHandler::IfOption { .. } => { 1296 FlowHandler::IfOption { .. } => {
1309 let none = make::expr_path(make_path_from_text("None")); 1297 let none = make::expr_path(make::ext::ident_path("None"));
1310 with_tail_expr(block, none) 1298 with_tail_expr(block, none)
1311 } 1299 }
1312 FlowHandler::MatchOption { .. } => map_tail_expr(block, |tail_expr| { 1300 FlowHandler::MatchOption { .. } => map_tail_expr(block, |tail_expr| {
1313 let some = make::expr_path(make_path_from_text("Some")); 1301 let some = make::expr_path(make::ext::ident_path("Some"));
1314 let args = make::arg_list(iter::once(tail_expr)); 1302 let args = make::arg_list(iter::once(tail_expr));
1315 make::expr_call(some, args) 1303 make::expr_call(some, args)
1316 }), 1304 }),
1317 FlowHandler::MatchResult { .. } => map_tail_expr(block, |tail_expr| { 1305 FlowHandler::MatchResult { .. } => map_tail_expr(block, |tail_expr| {
1318 let ok = make::expr_path(make_path_from_text("Ok")); 1306 let ok = make::expr_path(make::ext::ident_path("Ok"));
1319 let args = make::arg_list(iter::once(tail_expr)); 1307 let args = make::arg_list(iter::once(tail_expr));
1320 make::expr_call(ok, args) 1308 make::expr_call(ok, args)
1321 }), 1309 }),
@@ -1361,12 +1349,16 @@ fn rewrite_body_segment(
1361 syntax: &SyntaxNode, 1349 syntax: &SyntaxNode,
1362) -> SyntaxNode { 1350) -> SyntaxNode {
1363 let syntax = fix_param_usages(ctx, params, syntax); 1351 let syntax = fix_param_usages(ctx, params, syntax);
1364 update_external_control_flow(handler, &syntax) 1352 update_external_control_flow(handler, &syntax);
1353 syntax
1365} 1354}
1366 1355
1367/// change all usages to account for added `&`/`&mut` for some params 1356/// change all usages to account for added `&`/`&mut` for some params
1368fn fix_param_usages(ctx: &AssistContext, params: &[Param], syntax: &SyntaxNode) -> SyntaxNode { 1357fn fix_param_usages(ctx: &AssistContext, params: &[Param], syntax: &SyntaxNode) -> SyntaxNode {
1369 let mut rewriter = SyntaxRewriter::default(); 1358 let mut usages_for_param: Vec<(&Param, Vec<ast::Expr>)> = Vec::new();
1359
1360 let tm = TreeMutator::new(syntax);
1361
1370 for param in params { 1362 for param in params {
1371 if !param.kind().is_ref() { 1363 if !param.kind().is_ref() {
1372 continue; 1364 continue;
@@ -1376,101 +1368,100 @@ fn fix_param_usages(ctx: &AssistContext, params: &[Param], syntax: &SyntaxNode)
1376 let usages = usages 1368 let usages = usages
1377 .iter() 1369 .iter()
1378 .filter(|reference| syntax.text_range().contains_range(reference.range)) 1370 .filter(|reference| syntax.text_range().contains_range(reference.range))
1379 .filter_map(|reference| path_element_of_reference(syntax, reference)); 1371 .filter_map(|reference| path_element_of_reference(syntax, reference))
1380 for path in usages { 1372 .map(|expr| tm.make_mut(&expr));
1381 match path.syntax().ancestors().skip(1).find_map(ast::Expr::cast) { 1373
1374 usages_for_param.push((param, usages.collect()));
1375 }
1376
1377 let res = tm.make_syntax_mut(syntax);
1378
1379 for (param, usages) in usages_for_param {
1380 for usage in usages {
1381 match usage.syntax().ancestors().skip(1).find_map(ast::Expr::cast) {
1382 Some(ast::Expr::MethodCallExpr(_)) | Some(ast::Expr::FieldExpr(_)) => { 1382 Some(ast::Expr::MethodCallExpr(_)) | Some(ast::Expr::FieldExpr(_)) => {
1383 // do nothing 1383 // do nothing
1384 } 1384 }
1385 Some(ast::Expr::RefExpr(node)) 1385 Some(ast::Expr::RefExpr(node))
1386 if param.kind() == ParamKind::MutRef && node.mut_token().is_some() => 1386 if param.kind() == ParamKind::MutRef && node.mut_token().is_some() =>
1387 { 1387 {
1388 rewriter.replace_ast(&node.clone().into(), &node.expr().unwrap()); 1388 ted::replace(node.syntax(), node.expr().unwrap().syntax());
1389 } 1389 }
1390 Some(ast::Expr::RefExpr(node)) 1390 Some(ast::Expr::RefExpr(node))
1391 if param.kind() == ParamKind::SharedRef && node.mut_token().is_none() => 1391 if param.kind() == ParamKind::SharedRef && node.mut_token().is_none() =>
1392 { 1392 {
1393 rewriter.replace_ast(&node.clone().into(), &node.expr().unwrap()); 1393 ted::replace(node.syntax(), node.expr().unwrap().syntax());
1394 } 1394 }
1395 Some(_) | None => { 1395 Some(_) | None => {
1396 rewriter.replace_ast(&path, &make::expr_prefix(T![*], path.clone())); 1396 let p = &make::expr_prefix(T![*], usage.clone()).clone_for_update();
1397 ted::replace(usage.syntax(), p.syntax())
1397 } 1398 }
1398 }; 1399 }
1399 } 1400 }
1400 } 1401 }
1401 1402
1402 rewriter.rewrite(syntax) 1403 res
1403} 1404}
1404 1405
1405fn update_external_control_flow(handler: &FlowHandler, syntax: &SyntaxNode) -> SyntaxNode { 1406fn update_external_control_flow(handler: &FlowHandler, syntax: &SyntaxNode) {
1406 let mut rewriter = SyntaxRewriter::default();
1407
1408 let mut nested_loop = None; 1407 let mut nested_loop = None;
1409 let mut nested_scope = None; 1408 let mut nested_scope = None;
1410 for event in syntax.preorder() { 1409 for event in syntax.preorder() {
1411 let node = match event { 1410 match event {
1412 WalkEvent::Enter(e) => { 1411 WalkEvent::Enter(e) => match e.kind() {
1413 match e.kind() { 1412 SyntaxKind::LOOP_EXPR | SyntaxKind::WHILE_EXPR | SyntaxKind::FOR_EXPR => {
1414 SyntaxKind::LOOP_EXPR | SyntaxKind::WHILE_EXPR | SyntaxKind::FOR_EXPR => { 1413 if nested_loop.is_none() {
1415 if nested_loop.is_none() { 1414 nested_loop = Some(e.clone());
1416 nested_loop = Some(e.clone());
1417 }
1418 } 1415 }
1419 SyntaxKind::FN 1416 }
1420 | SyntaxKind::CONST 1417 SyntaxKind::FN
1421 | SyntaxKind::STATIC 1418 | SyntaxKind::CONST
1422 | SyntaxKind::IMPL 1419 | SyntaxKind::STATIC
1423 | SyntaxKind::MODULE => { 1420 | SyntaxKind::IMPL
1424 if nested_scope.is_none() { 1421 | SyntaxKind::MODULE => {
1425 nested_scope = Some(e.clone()); 1422 if nested_scope.is_none() {
1426 } 1423 nested_scope = Some(e.clone());
1427 } 1424 }
1428 _ => {}
1429 } 1425 }
1430 e 1426 _ => {}
1431 } 1427 },
1432 WalkEvent::Leave(e) => { 1428 WalkEvent::Leave(e) => {
1429 if nested_scope.is_none() {
1430 if let Some(expr) = ast::Expr::cast(e.clone()) {
1431 match expr {
1432 ast::Expr::ReturnExpr(return_expr) if nested_scope.is_none() => {
1433 let expr = return_expr.expr();
1434 if let Some(replacement) = make_rewritten_flow(handler, expr) {
1435 ted::replace(return_expr.syntax(), replacement.syntax())
1436 }
1437 }
1438 ast::Expr::BreakExpr(break_expr) if nested_loop.is_none() => {
1439 let expr = break_expr.expr();
1440 if let Some(replacement) = make_rewritten_flow(handler, expr) {
1441 ted::replace(break_expr.syntax(), replacement.syntax())
1442 }
1443 }
1444 ast::Expr::ContinueExpr(continue_expr) if nested_loop.is_none() => {
1445 if let Some(replacement) = make_rewritten_flow(handler, None) {
1446 ted::replace(continue_expr.syntax(), replacement.syntax())
1447 }
1448 }
1449 _ => {
1450 // do nothing
1451 }
1452 }
1453 }
1454 }
1455
1433 if nested_loop.as_ref() == Some(&e) { 1456 if nested_loop.as_ref() == Some(&e) {
1434 nested_loop = None; 1457 nested_loop = None;
1435 } 1458 }
1436 if nested_scope.as_ref() == Some(&e) { 1459 if nested_scope.as_ref() == Some(&e) {
1437 nested_scope = None; 1460 nested_scope = None;
1438 } 1461 }
1439 continue;
1440 } 1462 }
1441 }; 1463 };
1442 if nested_scope.is_some() {
1443 continue;
1444 }
1445 let expr = match ast::Expr::cast(node) {
1446 Some(e) => e,
1447 None => continue,
1448 };
1449 match expr {
1450 ast::Expr::ReturnExpr(return_expr) if nested_scope.is_none() => {
1451 let expr = return_expr.expr();
1452 if let Some(replacement) = make_rewritten_flow(handler, expr) {
1453 rewriter.replace_ast(&return_expr.into(), &replacement);
1454 }
1455 }
1456 ast::Expr::BreakExpr(break_expr) if nested_loop.is_none() => {
1457 let expr = break_expr.expr();
1458 if let Some(replacement) = make_rewritten_flow(handler, expr) {
1459 rewriter.replace_ast(&break_expr.into(), &replacement);
1460 }
1461 }
1462 ast::Expr::ContinueExpr(continue_expr) if nested_loop.is_none() => {
1463 if let Some(replacement) = make_rewritten_flow(handler, None) {
1464 rewriter.replace_ast(&continue_expr.into(), &replacement);
1465 }
1466 }
1467 _ => {
1468 // do nothing
1469 }
1470 }
1471 } 1464 }
1472
1473 rewriter.rewrite(syntax)
1474} 1465}
1475 1466
1476fn make_rewritten_flow(handler: &FlowHandler, arg_expr: Option<ast::Expr>) -> Option<ast::Expr> { 1467fn make_rewritten_flow(handler: &FlowHandler, arg_expr: Option<ast::Expr>) -> Option<ast::Expr> {
@@ -1480,16 +1471,16 @@ fn make_rewritten_flow(handler: &FlowHandler, arg_expr: Option<ast::Expr>) -> Op
1480 FlowHandler::IfOption { .. } => { 1471 FlowHandler::IfOption { .. } => {
1481 let expr = arg_expr.unwrap_or_else(|| make::expr_tuple(Vec::new())); 1472 let expr = arg_expr.unwrap_or_else(|| make::expr_tuple(Vec::new()));
1482 let args = make::arg_list(iter::once(expr)); 1473 let args = make::arg_list(iter::once(expr));
1483 make::expr_call(make::expr_path(make_path_from_text("Some")), args) 1474 make::expr_call(make::expr_path(make::ext::ident_path("Some")), args)
1484 } 1475 }
1485 FlowHandler::MatchOption { .. } => make::expr_path(make_path_from_text("None")), 1476 FlowHandler::MatchOption { .. } => make::expr_path(make::ext::ident_path("None")),
1486 FlowHandler::MatchResult { .. } => { 1477 FlowHandler::MatchResult { .. } => {
1487 let expr = arg_expr.unwrap_or_else(|| make::expr_tuple(Vec::new())); 1478 let expr = arg_expr.unwrap_or_else(|| make::expr_tuple(Vec::new()));
1488 let args = make::arg_list(iter::once(expr)); 1479 let args = make::arg_list(iter::once(expr));
1489 make::expr_call(make::expr_path(make_path_from_text("Err")), args) 1480 make::expr_call(make::expr_path(make::ext::ident_path("Err")), args)
1490 } 1481 }
1491 }; 1482 };
1492 Some(make::expr_return(Some(value))) 1483 Some(make::expr_return(Some(value)).clone_for_update())
1493} 1484}
1494 1485
1495#[cfg(test)] 1486#[cfg(test)]