Skip to content

Commit 35e0d80

Browse files
committed
Deunwrap extract_function
1 parent 326f37e commit 35e0d80

File tree

1 file changed

+63
-56
lines changed

1 file changed

+63
-56
lines changed

crates/ide-assists/src/handlers/extract_function.rs

Lines changed: 63 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -103,50 +103,49 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
103103

104104
let scope = ImportScope::find_insert_use_container(&node, &ctx.sema)?;
105105

106+
let outliving_locals: Vec<_> = ret_values.collect();
107+
if stdx::never!(!outliving_locals.is_empty() && !ret_ty.is_unit()) {
108+
// We should not have variables that outlive body if we have expression block
109+
return None;
110+
}
111+
112+
let params = body.extracted_function_params(ctx, &container_info, locals_used.iter().copied());
113+
114+
let name = make_function_name(&semantics_scope);
115+
116+
let has_impl_wrapper =
117+
insert_after.ancestors().any(|a| a.kind() == SyntaxKind::IMPL && a != insert_after);
118+
119+
let fun = Function {
120+
name,
121+
self_param,
122+
params,
123+
control_flow,
124+
ret_ty,
125+
body,
126+
outliving_locals,
127+
contains_tail_expr,
128+
mods: container_info,
129+
};
130+
131+
let new_indent = IndentLevel::from_node(&insert_after);
132+
let old_indent = fun.body.indent_level();
133+
134+
let fn_def = match fun.self_param_adt(ctx) {
135+
Some(adt) if anchor == Anchor::Method && !has_impl_wrapper => {
136+
let fn_def = format_function(ctx, module, &fun, old_indent, new_indent + 1)?;
137+
generate_impl_text(&adt, &fn_def).replace("{\n\n", "{")
138+
}
139+
_ => format_function(ctx, module, &fun, old_indent, new_indent)?,
140+
};
141+
106142
acc.add(
107143
AssistId("extract_function", crate::AssistKind::RefactorExtract),
108144
"Extract into function",
109145
target_range,
110146
move |builder| {
111-
let outliving_locals: Vec<_> = ret_values.collect();
112-
if stdx::never!(!outliving_locals.is_empty() && !ret_ty.is_unit()) {
113-
// We should not have variables that outlive body if we have expression block
114-
return;
115-
}
116-
117-
let params =
118-
body.extracted_function_params(ctx, &container_info, locals_used.iter().copied());
119-
120-
let name = make_function_name(&semantics_scope);
121-
122-
let fun = Function {
123-
name,
124-
self_param,
125-
params,
126-
control_flow,
127-
ret_ty,
128-
body,
129-
outliving_locals,
130-
contains_tail_expr,
131-
mods: container_info,
132-
};
133-
134-
let new_indent = IndentLevel::from_node(&insert_after);
135-
let old_indent = fun.body.indent_level();
136-
137147
builder.replace(target_range, make_call(ctx, &fun, old_indent));
138148

139-
let has_impl_wrapper =
140-
insert_after.ancestors().any(|a| a.kind() == SyntaxKind::IMPL && a != insert_after);
141-
142-
let fn_def = match fun.self_param_adt(ctx) {
143-
Some(adt) if anchor == Anchor::Method && !has_impl_wrapper => {
144-
let fn_def = format_function(ctx, module, &fun, old_indent, new_indent + 1);
145-
generate_impl_text(&adt, &fn_def).replace("{\n\n", "{")
146-
}
147-
_ => format_function(ctx, module, &fun, old_indent, new_indent),
148-
};
149-
150149
if fn_def.contains("ControlFlow") {
151150
let scope = match scope {
152151
ImportScope::File(it) => ImportScope::File(builder.make_mut(it)),
@@ -1501,13 +1500,13 @@ fn format_function(
15011500
fun: &Function,
15021501
old_indent: IndentLevel,
15031502
new_indent: IndentLevel,
1504-
) -> String {
1503+
) -> Option<String> {
15051504
let mut fn_def = String::new();
15061505

15071506
let fun_name = &fun.name;
15081507
let params = fun.make_param_list(ctx, module);
15091508
let ret_ty = fun.make_ret_ty(ctx, module);
1510-
let body = make_body(ctx, old_indent, new_indent, fun);
1509+
let body = make_body(ctx, old_indent, new_indent, fun)?;
15111510
let const_kw = if fun.mods.is_const { "const " } else { "" };
15121511
let async_kw = if fun.control_flow.is_async { "async " } else { "" };
15131512
let unsafe_kw = if fun.control_flow.is_unsafe { "unsafe " } else { "" };
@@ -1535,7 +1534,7 @@ fn format_function(
15351534

15361535
format_to!(fn_def, " {body}");
15371536

1538-
fn_def
1537+
Some(fn_def)
15391538
}
15401539

15411540
fn make_generic_params_and_where_clause(
@@ -1721,14 +1720,14 @@ fn make_body(
17211720
old_indent: IndentLevel,
17221721
new_indent: IndentLevel,
17231722
fun: &Function,
1724-
) -> ast::BlockExpr {
1723+
) -> Option<ast::BlockExpr> {
17251724
let ret_ty = fun.return_type(ctx);
17261725
let handler = FlowHandler::from_ret_ty(fun, &ret_ty);
17271726

17281727
let block = match &fun.body {
17291728
FunctionBody::Expr(expr) => {
1730-
let expr = rewrite_body_segment(ctx, &fun.params, &handler, expr.syntax());
1731-
let expr = ast::Expr::cast(expr).unwrap();
1729+
let expr = rewrite_body_segment(ctx, &fun.params, &handler, expr.syntax())?;
1730+
let expr = ast::Expr::cast(expr)?;
17321731
match expr {
17331732
ast::Expr::BlockExpr(block) => {
17341733
// If the extracted expression is itself a block, there is no need to wrap it inside another block.
@@ -1749,12 +1748,16 @@ fn make_body(
17491748
.children_with_tokens()
17501749
.filter(|it| text_range.contains_range(it.text_range()))
17511750
.map(|it| match &it {
1752-
syntax::NodeOrToken::Node(n) => syntax::NodeOrToken::Node(
1753-
rewrite_body_segment(ctx, &fun.params, &handler, n),
1754-
),
1755-
_ => it,
1751+
syntax::NodeOrToken::Node(n) => {
1752+
let bs = rewrite_body_segment(ctx, &fun.params, &handler, n);
1753+
match bs {
1754+
Some(n) => Some(syntax::NodeOrToken::Node(n)),
1755+
None => None,
1756+
}
1757+
}
1758+
_ => Some(it),
17561759
})
1757-
.collect();
1760+
.collect::<Option<_>>()?;
17581761

17591762
let mut tail_expr = match &elements.last() {
17601763
Some(syntax::NodeOrToken::Node(node)) if ast::Expr::can_cast(node.kind()) => {
@@ -1838,7 +1841,7 @@ fn make_body(
18381841
}),
18391842
};
18401843

1841-
block.indent(new_indent)
1844+
Some(block.indent(new_indent))
18421845
}
18431846

18441847
fn map_tail_expr(block: ast::BlockExpr, f: impl FnOnce(ast::Expr) -> ast::Expr) -> ast::BlockExpr {
@@ -1896,14 +1899,18 @@ fn rewrite_body_segment(
18961899
params: &[Param],
18971900
handler: &FlowHandler,
18981901
syntax: &SyntaxNode,
1899-
) -> SyntaxNode {
1900-
let syntax = fix_param_usages(ctx, params, syntax);
1902+
) -> Option<SyntaxNode> {
1903+
let syntax = fix_param_usages(ctx, params, syntax)?;
19011904
update_external_control_flow(handler, &syntax);
1902-
syntax
1905+
Some(syntax)
19031906
}
19041907

19051908
/// change all usages to account for added `&`/`&mut` for some params
1906-
fn fix_param_usages(ctx: &AssistContext<'_>, params: &[Param], syntax: &SyntaxNode) -> SyntaxNode {
1909+
fn fix_param_usages(
1910+
ctx: &AssistContext<'_>,
1911+
params: &[Param],
1912+
syntax: &SyntaxNode,
1913+
) -> Option<SyntaxNode> {
19071914
let mut usages_for_param: Vec<(&Param, Vec<ast::Expr>)> = Vec::new();
19081915

19091916
let tm = TreeMutator::new(syntax);
@@ -1934,12 +1941,12 @@ fn fix_param_usages(ctx: &AssistContext<'_>, params: &[Param], syntax: &SyntaxNo
19341941
Some(ast::Expr::RefExpr(node))
19351942
if param.kind() == ParamKind::MutRef && node.mut_token().is_some() =>
19361943
{
1937-
ted::replace(node.syntax(), node.expr().unwrap().syntax());
1944+
ted::replace(node.syntax(), node.expr()?.syntax());
19381945
}
19391946
Some(ast::Expr::RefExpr(node))
19401947
if param.kind() == ParamKind::SharedRef && node.mut_token().is_none() =>
19411948
{
1942-
ted::replace(node.syntax(), node.expr().unwrap().syntax());
1949+
ted::replace(node.syntax(), node.expr()?.syntax());
19431950
}
19441951
Some(_) | None => {
19451952
let p = &make::expr_prefix(T![*], usage.clone()).clone_for_update();
@@ -1949,7 +1956,7 @@ fn fix_param_usages(ctx: &AssistContext<'_>, params: &[Param], syntax: &SyntaxNo
19491956
}
19501957
}
19511958

1952-
res
1959+
Some(res)
19531960
}
19541961

19551962
fn update_external_control_flow(handler: &FlowHandler, syntax: &SyntaxNode) {

0 commit comments

Comments
 (0)