Skip to content

Commit 80163d3

Browse files
committed
internal: Migrate unwrap_return_type assist to use SyntaxEditor
Also changes `make::expr_empty_block()` to return `ast::BlockExpr` instead of `ast::Expr`
1 parent 115d411 commit 80163d3

File tree

4 files changed

+105
-57
lines changed

4 files changed

+105
-57
lines changed

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

+74-54
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1+
use either::Either;
12
use ide_db::{
23
famous_defs::FamousDefs,
34
syntax_helpers::node_ext::{for_each_tail_expr, walk_expr},
45
};
5-
use itertools::Itertools;
66
use syntax::{
7-
ast::{self, Expr, HasGenericArgs},
8-
match_ast, AstNode, NodeOrToken, SyntaxKind, TextRange,
7+
ast::{self, syntax_factory::SyntaxFactory, HasArgList, HasGenericArgs},
8+
match_ast, AstNode, NodeOrToken, SyntaxKind,
99
};
1010

1111
use crate::{AssistContext, AssistId, AssistKind, Assists};
@@ -39,11 +39,11 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
3939
pub(crate) fn unwrap_return_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
4040
let ret_type = ctx.find_node_at_offset::<ast::RetType>()?;
4141
let parent = ret_type.syntax().parent()?;
42-
let body = match_ast! {
42+
let body_expr = match_ast! {
4343
match parent {
44-
ast::Fn(func) => func.body()?,
44+
ast::Fn(func) => func.body()?.into(),
4545
ast::ClosureExpr(closure) => match closure.body()? {
46-
Expr::BlockExpr(block) => block,
46+
ast::Expr::BlockExpr(block) => block.into(),
4747
// closures require a block when a return type is specified
4848
_ => return None,
4949
},
@@ -65,72 +65,96 @@ pub(crate) fn unwrap_return_type(acc: &mut Assists, ctx: &AssistContext<'_>) ->
6565
let happy_type = extract_wrapped_type(type_ref)?;
6666

6767
acc.add(kind.assist_id(), kind.label(), type_ref.syntax().text_range(), |builder| {
68-
let body = ast::Expr::BlockExpr(body);
68+
let mut editor = builder.make_editor(&parent);
69+
let make = SyntaxFactory::new();
6970

7071
let mut exprs_to_unwrap = Vec::new();
7172
let tail_cb = &mut |e: &_| tail_cb_impl(&mut exprs_to_unwrap, e);
72-
walk_expr(&body, &mut |expr| {
73-
if let Expr::ReturnExpr(ret_expr) = expr {
73+
walk_expr(&body_expr, &mut |expr| {
74+
if let ast::Expr::ReturnExpr(ret_expr) = expr {
7475
if let Some(ret_expr_arg) = &ret_expr.expr() {
7576
for_each_tail_expr(ret_expr_arg, tail_cb);
7677
}
7778
}
7879
});
79-
for_each_tail_expr(&body, tail_cb);
80+
for_each_tail_expr(&body_expr, tail_cb);
8081

8182
let is_unit_type = is_unit_type(&happy_type);
8283
if is_unit_type {
83-
let mut text_range = ret_type.syntax().text_range();
84-
8584
if let Some(NodeOrToken::Token(token)) = ret_type.syntax().next_sibling_or_token() {
8685
if token.kind() == SyntaxKind::WHITESPACE {
87-
text_range = TextRange::new(text_range.start(), token.text_range().end());
86+
editor.delete(token);
8887
}
8988
}
9089

91-
builder.delete(text_range);
90+
editor.delete(ret_type.syntax());
9291
} else {
93-
builder.replace(type_ref.syntax().text_range(), happy_type.syntax().text());
92+
// FIXME: remove `clone_for_update` when `SyntaxEditor` handles it for us
93+
editor.replace(type_ref.syntax(), happy_type.syntax().clone_for_update());
9494
}
9595

96-
for ret_expr_arg in exprs_to_unwrap {
97-
let ret_expr_str = ret_expr_arg.to_string();
98-
99-
let needs_replacing = match kind {
100-
UnwrapperKind::Option => ret_expr_str.starts_with("Some("),
101-
UnwrapperKind::Result => {
102-
ret_expr_str.starts_with("Ok(") || ret_expr_str.starts_with("Err(")
103-
}
104-
};
96+
for tail_expr in exprs_to_unwrap {
97+
match &tail_expr {
98+
ast::Expr::CallExpr(call_expr) => {
99+
let ast::Expr::PathExpr(path_expr) = call_expr.expr().unwrap() else {
100+
continue;
101+
};
102+
103+
let path_str = path_expr.path().unwrap().to_string();
104+
let needs_replacing = match kind {
105+
UnwrapperKind::Option => path_str == "Some",
106+
UnwrapperKind::Result => path_str == "Ok" || path_str == "Err",
107+
};
108+
109+
if !needs_replacing {
110+
continue;
111+
}
105112

106-
if needs_replacing {
107-
let arg_list = ret_expr_arg.syntax().children().find_map(ast::ArgList::cast);
108-
if let Some(arg_list) = arg_list {
113+
let arg_list = call_expr.arg_list().unwrap();
109114
if is_unit_type {
110-
match ret_expr_arg.syntax().prev_sibling_or_token() {
111-
// Useful to delete the entire line without leaving trailing whitespaces
112-
Some(whitespace) => {
113-
let new_range = TextRange::new(
114-
whitespace.text_range().start(),
115-
ret_expr_arg.syntax().text_range().end(),
116-
);
117-
builder.delete(new_range);
115+
let tail_parent = tail_expr
116+
.syntax()
117+
.parent()
118+
.and_then(Either::<ast::ReturnExpr, ast::StmtList>::cast)
119+
.unwrap();
120+
match tail_parent {
121+
Either::Left(ret_expr) => {
122+
editor.replace(ret_expr.syntax(), make.expr_return(None).syntax())
118123
}
119-
None => {
120-
builder.delete(ret_expr_arg.syntax().text_range());
124+
Either::Right(stmt_list) => {
125+
let new_block = if stmt_list.statements().next().is_none() {
126+
make.expr_empty_block()
127+
} else {
128+
make.block_expr(stmt_list.statements(), None)
129+
};
130+
editor.replace(
131+
stmt_list.syntax(),
132+
new_block.stmt_list().unwrap().syntax(),
133+
);
121134
}
122135
}
123-
} else {
124-
builder.replace(
125-
ret_expr_arg.syntax().text_range(),
126-
arg_list.args().join(", "),
127-
);
136+
} else if let Some(first_arg) = arg_list.args().next() {
137+
// FIXME: remove `clone_for_update` when `SyntaxEditor` handles it for us
138+
editor.replace(tail_expr.syntax(), first_arg.syntax().clone_for_update());
128139
}
129140
}
130-
} else if matches!(kind, UnwrapperKind::Option if ret_expr_str == "None") {
131-
builder.replace(ret_expr_arg.syntax().text_range(), "()");
141+
ast::Expr::PathExpr(path_expr) => {
142+
let UnwrapperKind::Option = kind else {
143+
continue;
144+
};
145+
146+
if path_expr.path().unwrap().to_string() != "None" {
147+
continue;
148+
}
149+
150+
editor.replace(path_expr.syntax(), make.expr_unit().syntax());
151+
}
152+
_ => (),
132153
}
133154
}
155+
156+
editor.add_mappings(make.finish_with_mappings());
157+
builder.add_file_edits(ctx.file_id(), editor);
134158
})
135159
}
136160

@@ -168,12 +192,12 @@ impl UnwrapperKind {
168192

169193
fn tail_cb_impl(acc: &mut Vec<ast::Expr>, e: &ast::Expr) {
170194
match e {
171-
Expr::BreakExpr(break_expr) => {
195+
ast::Expr::BreakExpr(break_expr) => {
172196
if let Some(break_expr_arg) = break_expr.expr() {
173197
for_each_tail_expr(&break_expr_arg, &mut |e| tail_cb_impl(acc, e))
174198
}
175199
}
176-
Expr::ReturnExpr(_) => {
200+
ast::Expr::ReturnExpr(_) => {
177201
// all return expressions have already been handled by the walk loop
178202
}
179203
e => acc.push(e.clone()),
@@ -238,8 +262,7 @@ fn foo() -> Option<()$0> {
238262
}
239263
"#,
240264
r#"
241-
fn foo() {
242-
}
265+
fn foo() {}
243266
"#,
244267
"Unwrap Option return type",
245268
);
@@ -254,8 +277,7 @@ fn foo() -> Option<()$0>{
254277
}
255278
"#,
256279
r#"
257-
fn foo() {
258-
}
280+
fn foo() {}
259281
"#,
260282
"Unwrap Option return type",
261283
);
@@ -1262,8 +1284,7 @@ fn foo() -> Result<(), Box<dyn Error$0>> {
12621284
}
12631285
"#,
12641286
r#"
1265-
fn foo() {
1266-
}
1287+
fn foo() {}
12671288
"#,
12681289
"Unwrap Result return type",
12691290
);
@@ -1278,8 +1299,7 @@ fn foo() -> Result<(), Box<dyn Error$0>>{
12781299
}
12791300
"#,
12801301
r#"
1281-
fn foo() {
1282-
}
1302+
fn foo() {}
12831303
"#,
12841304
"Unwrap Result return type",
12851305
);

crates/ide-assists/src/utils/gen_trait_fn_body.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -599,7 +599,7 @@ fn gen_partial_ord(adt: &ast::Adt, func: &ast::Fn, trait_ref: Option<TraitRef>)
599599
let variant_name =
600600
make::path_pat(make::ext::path_from_idents(["core", "cmp", "Ordering", "Equal"])?);
601601
let lhs = make::tuple_struct_pat(make::ext::path_from_idents(["Some"])?, [variant_name]);
602-
arms.push(make::match_arm(Some(lhs.into()), None, make::expr_empty_block()));
602+
arms.push(make::match_arm(Some(lhs.into()), None, make::expr_empty_block().into()));
603603

604604
arms.push(make::match_arm(
605605
[make::ident_pat(false, false, make::name("ord")).into()],

crates/syntax/src/ast/make.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -555,8 +555,8 @@ pub fn expr_const_value(text: &str) -> ast::ConstArg {
555555
ast_from_text(&format!("trait Foo<const N: usize = {text}> {{}}"))
556556
}
557557

558-
pub fn expr_empty_block() -> ast::Expr {
559-
expr_from_text("{}")
558+
pub fn expr_empty_block() -> ast::BlockExpr {
559+
ast_from_text("const C: () = {};")
560560
}
561561
pub fn expr_path(path: ast::Path) -> ast::Expr {
562562
expr_from_text(&path.to_string())

crates/syntax/src/ast/syntax_factory/constructors.rs

+28
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,18 @@ impl SyntaxFactory {
172172
ast
173173
}
174174

175+
pub fn expr_empty_block(&self) -> ast::BlockExpr {
176+
make::expr_empty_block().clone_for_update()
177+
}
178+
179+
pub fn expr_unit(&self) -> ast::TupleExpr {
180+
let ast::Expr::TupleExpr(ast) = make::expr_unit().clone_for_update() else {
181+
unreachable!()
182+
};
183+
184+
ast
185+
}
186+
175187
pub fn expr_path(&self, path: ast::Path) -> ast::Expr {
176188
let ast::Expr::PathExpr(ast) = make::expr_path(path.clone()).clone_for_update() else {
177189
unreachable!()
@@ -233,6 +245,22 @@ impl SyntaxFactory {
233245
ast.into()
234246
}
235247

248+
pub fn expr_return(&self, expr: Option<ast::Expr>) -> ast::ReturnExpr {
249+
let ast::Expr::ReturnExpr(ast) = make::expr_return(expr.clone()).clone_for_update() else {
250+
unreachable!()
251+
};
252+
253+
if let Some(mut mapping) = self.mappings() {
254+
let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
255+
if let Some(input) = expr {
256+
builder.map_node(input.syntax().clone(), ast.expr().unwrap().syntax().clone());
257+
}
258+
builder.finish(&mut mapping);
259+
}
260+
261+
ast
262+
}
263+
236264
pub fn let_stmt(
237265
&self,
238266
pattern: ast::Pat,

0 commit comments

Comments
 (0)