Skip to content

Commit 9dd1fd8

Browse files
committed
feat: offer on if-expr with else-if for convert_to_guarded_return
Example --- ```rust fn main() { for n in ns { if$0 let Some(n) = n { foo(n); bar(); } else if cond() { return } else { break } } } ``` **Before this PR** Assist not applicable **After this PR** ```rust fn main() { for n in ns { let Some(n) = n else { if cond() { return } else { break } }; foo(n); bar(); } } ```
1 parent 11b719d commit 9dd1fd8

1 file changed

Lines changed: 183 additions & 17 deletions

File tree

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

Lines changed: 183 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -72,12 +72,6 @@ fn if_expr_to_guarded_return(
7272
ctx: &AssistContext<'_>,
7373
) -> Option<()> {
7474
let make = SyntaxFactory::without_mappings();
75-
let else_block = match if_expr.else_branch() {
76-
Some(ast::ElseBranch::Block(block_expr)) => Some(block_expr),
77-
Some(_) => return None,
78-
_ => None,
79-
};
80-
8175
let cond = if_expr.condition()?;
8276

8377
let if_token_range = if_expr.if_token()?.text_range();
@@ -102,7 +96,7 @@ fn if_expr_to_guarded_return(
10296
}
10397

10498
let container = container_of(&parent_block)?;
105-
let else_block = ElseBlock::new(&ctx.sema, else_block, &container)?;
99+
let else_block = ElseBlock::new(&ctx.sema, if_expr.else_branch(), &container)?;
106100

107101
if parent_block.tail_expr() != Some(if_expr.clone().into())
108102
&& !(else_block.is_never_block
@@ -237,37 +231,47 @@ fn container_of(block: &ast::BlockExpr) -> Option<SyntaxNode> {
237231
}
238232

239233
struct ElseBlock<'db> {
240-
exist_else_block: Option<ast::BlockExpr>,
234+
exist_else_branch: Option<ast::ElseBranch>,
241235
is_never_block: bool,
242236
kind: EarlyKind<'db>,
243237
}
244238

245239
impl<'db> ElseBlock<'db> {
246240
fn new(
247241
sema: &Semantics<'db, RootDatabase>,
248-
exist_else_block: Option<ast::BlockExpr>,
242+
exist_else_branch: Option<ast::ElseBranch>,
249243
parent_container: &SyntaxNode,
250244
) -> Option<Self> {
251-
let is_never_block = exist_else_block.as_ref().is_some_and(|it| is_never_block(sema, it));
245+
let is_never_block =
246+
exist_else_branch.as_ref().is_some_and(|it| is_never_else_branch(sema, it));
252247
let kind = EarlyKind::from_node(parent_container, sema)?;
253248

254-
Some(Self { exist_else_block, is_never_block, kind })
249+
Some(Self { exist_else_branch, is_never_block, kind })
250+
}
251+
252+
fn make_else_block_from_exist_branch(&self, make: &SyntaxFactory) -> Option<ast::BlockExpr> {
253+
match self.exist_else_branch.as_ref()? {
254+
ast::ElseBranch::Block(block_expr) => Some(block_expr.reset_indent()),
255+
ast::ElseBranch::IfExpr(if_expr) => {
256+
Some(make.block_expr(None, Some(if_expr.reset_indent().indent(1.into()).into())))
257+
}
258+
}
255259
}
256260

257261
fn make_early_block(
258262
self,
259263
sema: &Semantics<'_, RootDatabase>,
260264
make: &SyntaxFactory,
261265
) -> ast::BlockExpr {
262-
let Some(block_expr) = self.exist_else_block else {
266+
let Some(block_expr) = self.make_else_block_from_exist_branch(make) else {
263267
return make.tail_only_block_expr(self.kind.make_early_expr(sema, make, None));
264268
};
265269

266270
if self.is_never_block {
267-
return block_expr.reset_indent();
271+
return block_expr;
268272
}
269273

270-
let (editor, block_expr) = SyntaxEditor::with_ast_node(&block_expr.reset_indent());
274+
let (editor, block_expr) = SyntaxEditor::with_ast_node(&block_expr);
271275
let make = editor.make();
272276

273277
let last_stmt = block_expr.statements().last().map(|it| it.syntax().clone());
@@ -284,8 +288,8 @@ impl<'db> ElseBlock<'db> {
284288
editor.replace(tail_expr.syntax(), early_expr.syntax());
285289
} else {
286290
let last_stmt = match block_expr.tail_expr() {
287-
Some(expr) => make.expr_stmt(expr).syntax().clone(),
288-
None => last_element.clone(),
291+
Some(expr) if !expr.is_block_like() => make.expr_stmt(expr).syntax().clone(),
292+
_ => last_element.clone(),
289293
};
290294
let whitespace =
291295
make.whitespace(&whitespace.map_or(String::new(), |it| it.to_string()));
@@ -401,6 +405,27 @@ fn is_early_block(then_block: &ast::StmtList) -> bool {
401405
|| then_block.statements().filter_map(into_expr).any(is_early_expr)
402406
}
403407

408+
fn is_never_else_branch(sema: &Semantics<'_, RootDatabase>, it: &ast::ElseBranch) -> bool {
409+
match it {
410+
ast::ElseBranch::Block(block_expr) => is_never_block(sema, block_expr),
411+
ast::ElseBranch::IfExpr(if_expr) => {
412+
let mut if_exprs =
413+
std::iter::successors(Some(if_expr.clone()), |it| match it.else_branch()? {
414+
ast::ElseBranch::Block(_) => None,
415+
ast::ElseBranch::IfExpr(if_expr) => Some(if_expr),
416+
});
417+
if_exprs.all(|it| {
418+
let else_is_never = match it.else_branch() {
419+
None => false,
420+
Some(ast::ElseBranch::IfExpr(_)) => true,
421+
Some(ast::ElseBranch::Block(block)) => is_never_block(sema, &block),
422+
};
423+
else_is_never && it.then_branch().is_some_and(|it| is_never_block(sema, &it))
424+
})
425+
}
426+
}
427+
}
428+
404429
#[cfg(test)]
405430
mod tests {
406431
use crate::tests::{check_assist, check_assist_not_applicable};
@@ -1024,6 +1049,42 @@ fn main() {
10241049
);
10251050
}
10261051

1052+
#[test]
1053+
fn convert_inside_loop_with_else_if() {
1054+
check_assist(
1055+
convert_to_guarded_return,
1056+
r#"
1057+
fn main() {
1058+
loop {
1059+
if$0 guard() {
1060+
foo();
1061+
bar();
1062+
} else if cond() {
1063+
break;
1064+
} else {
1065+
return
1066+
}
1067+
}
1068+
}
1069+
"#,
1070+
r#"
1071+
fn main() {
1072+
loop {
1073+
if !guard() {
1074+
if cond() {
1075+
break;
1076+
} else {
1077+
return
1078+
}
1079+
}
1080+
foo();
1081+
bar();
1082+
}
1083+
}
1084+
"#,
1085+
);
1086+
}
1087+
10271088
#[test]
10281089
fn convert_let_inside_loop() {
10291090
check_assist(
@@ -1055,6 +1116,7 @@ fn main() {
10551116
check_assist(
10561117
convert_to_guarded_return,
10571118
r#"
1119+
//- minicore: iterator
10581120
fn main() {
10591121
for n in ns {
10601122
if$0 let Some(n) = n {
@@ -1107,6 +1169,7 @@ fn main() {
11071169
check_assist(
11081170
convert_to_guarded_return,
11091171
r#"
1172+
//- minicore: iterator
11101173
fn main() {
11111174
for n in ns {
11121175
if$0 let Some(n) = n {
@@ -1133,6 +1196,109 @@ fn main() {
11331196
);
11341197
}
11351198

1199+
#[test]
1200+
fn convert_let_inside_for_with_else_if() {
1201+
check_assist(
1202+
convert_to_guarded_return,
1203+
r#"
1204+
//- minicore: iterator
1205+
fn main() {
1206+
for n in ns {
1207+
if$0 let Some(n) = n {
1208+
foo(n);
1209+
bar();
1210+
} else if cond() {
1211+
return
1212+
} else {
1213+
baz()
1214+
}
1215+
}
1216+
}
1217+
"#,
1218+
r#"
1219+
fn main() {
1220+
for n in ns {
1221+
let Some(n) = n else {
1222+
if cond() {
1223+
return
1224+
} else {
1225+
baz()
1226+
}
1227+
continue
1228+
};
1229+
foo(n);
1230+
bar();
1231+
}
1232+
}
1233+
"#,
1234+
);
1235+
1236+
check_assist(
1237+
convert_to_guarded_return,
1238+
r#"
1239+
//- minicore: iterator
1240+
fn main() {
1241+
for n in ns {
1242+
if$0 let Some(n) = n {
1243+
foo(n);
1244+
bar();
1245+
} else if cond() {
1246+
return
1247+
}
1248+
}
1249+
}
1250+
"#,
1251+
r#"
1252+
fn main() {
1253+
for n in ns {
1254+
let Some(n) = n else {
1255+
if cond() {
1256+
return
1257+
}
1258+
continue
1259+
};
1260+
foo(n);
1261+
bar();
1262+
}
1263+
}
1264+
"#,
1265+
);
1266+
1267+
check_assist(
1268+
convert_to_guarded_return,
1269+
r#"
1270+
//- minicore: iterator
1271+
fn main() {
1272+
for n in ns {
1273+
if$0 let Some(n) = n {
1274+
foo(n);
1275+
bar();
1276+
} else if cond() {
1277+
return
1278+
} else {
1279+
break
1280+
}
1281+
}
1282+
}
1283+
"#,
1284+
r#"
1285+
fn main() {
1286+
for n in ns {
1287+
let Some(n) = n else {
1288+
if cond() {
1289+
return
1290+
} else {
1291+
break
1292+
}
1293+
};
1294+
foo(n);
1295+
bar();
1296+
}
1297+
}
1298+
"#,
1299+
);
1300+
}
1301+
11361302
#[test]
11371303
fn convert_let_stmt_inside_fn() {
11381304
check_assist(
@@ -1415,7 +1581,7 @@ fn main() {
14151581
}
14161582

14171583
#[test]
1418-
fn ignore_else_if() {
1584+
fn ignore_on_else_if() {
14191585
check_assist_not_applicable(
14201586
convert_to_guarded_return,
14211587
r#"

0 commit comments

Comments
 (0)