Skip to content

Commit 3b5e529

Browse files
committed
Add else-block support for convert_to_guarded_return
Add support for else-block of never-type for `convert_to_guarded_return` Example --- ```rust fn main() { if$0 let Ok(x) = Err(92) { foo(x); } else { return } } ``` **Before this PR**: Assist not applicable **After this PR**: ```rust fn main() { let Ok(x) = Err(92) else { return }; foo(x); } ```
1 parent a56e577 commit 3b5e529

File tree

2 files changed

+90
-15
lines changed

2 files changed

+90
-15
lines changed

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

Lines changed: 74 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use syntax::{
1717
use crate::{
1818
AssistId,
1919
assist_context::{AssistContext, Assists},
20-
utils::invert_boolean_expression_legacy,
20+
utils::{invert_boolean_expression_legacy, is_never_block},
2121
};
2222

2323
// Assist: convert_to_guarded_return
@@ -54,9 +54,13 @@ fn if_expr_to_guarded_return(
5454
acc: &mut Assists,
5555
ctx: &AssistContext<'_>,
5656
) -> Option<()> {
57-
if if_expr.else_branch().is_some() {
58-
return None;
59-
}
57+
let else_block = match if_expr.else_branch() {
58+
Some(ast::ElseBranch::Block(block_expr)) if is_never_block(&ctx.sema, &block_expr) => {
59+
Some(block_expr)
60+
}
61+
Some(_) => return None,
62+
_ => None,
63+
};
6064

6165
let cond = if_expr.condition()?;
6266

@@ -96,7 +100,11 @@ fn if_expr_to_guarded_return(
96100

97101
let parent_container = parent_block.syntax().parent()?;
98102

99-
let early_expression: ast::Expr = early_expression(parent_container, &ctx.sema)?;
103+
let early_expression = else_block
104+
.or_else(|| {
105+
early_expression(parent_container, &ctx.sema).map(ast::make::tail_only_block_expr)
106+
})?
107+
.reset_indent();
100108

101109
then_block.syntax().first_child_or_token().map(|t| t.kind() == T!['{'])?;
102110

@@ -123,21 +131,14 @@ fn if_expr_to_guarded_return(
123131
&& let (Some(pat), Some(expr)) = (let_expr.pat(), let_expr.expr())
124132
{
125133
// If-let.
126-
let let_else_stmt = make::let_else_stmt(
127-
pat,
128-
None,
129-
expr,
130-
ast::make::tail_only_block_expr(early_expression.clone()),
131-
);
134+
let let_else_stmt =
135+
make::let_else_stmt(pat, None, expr, early_expression.clone());
132136
let let_else_stmt = let_else_stmt.indent(if_indent_level);
133137
let_else_stmt.syntax().clone()
134138
} else {
135139
// If.
136140
let new_expr = {
137-
let then_branch = make::block_expr(
138-
once(make::expr_stmt(early_expression.clone()).into()),
139-
None,
140-
);
141+
let then_branch = clean_stmt_block(&early_expression);
141142
let cond = invert_boolean_expression_legacy(expr);
142143
make::expr_if(cond, then_branch, None).indent(if_indent_level)
143144
};
@@ -272,6 +273,17 @@ fn flat_let_chain(mut expr: ast::Expr) -> Vec<ast::Expr> {
272273
chains
273274
}
274275

276+
fn clean_stmt_block(block: &ast::BlockExpr) -> ast::BlockExpr {
277+
if block.statements().next().is_none()
278+
&& let Some(tail_expr) = block.tail_expr()
279+
&& block.modifier().is_none()
280+
{
281+
make::block_expr(once(make::expr_stmt(tail_expr).into()), None)
282+
} else {
283+
block.clone()
284+
}
285+
}
286+
275287
#[cfg(test)]
276288
mod tests {
277289
use crate::tests::{check_assist, check_assist_not_applicable};
@@ -421,6 +433,53 @@ fn main() {
421433
);
422434
}
423435

436+
#[test]
437+
fn convert_if_let_has_never_type_else_block() {
438+
check_assist(
439+
convert_to_guarded_return,
440+
r#"
441+
fn main() {
442+
if$0 let Ok(x) = Err(92) {
443+
foo(x);
444+
} else {
445+
// needless comment
446+
return;
447+
}
448+
}
449+
"#,
450+
r#"
451+
fn main() {
452+
let Ok(x) = Err(92) else {
453+
// needless comment
454+
return;
455+
};
456+
foo(x);
457+
}
458+
"#,
459+
);
460+
461+
check_assist(
462+
convert_to_guarded_return,
463+
r#"
464+
fn main() {
465+
if$0 let Ok(x) = Err(92) {
466+
foo(x);
467+
} else {
468+
return
469+
}
470+
}
471+
"#,
472+
r#"
473+
fn main() {
474+
let Ok(x) = Err(92) else {
475+
return
476+
};
477+
foo(x);
478+
}
479+
"#,
480+
);
481+
}
482+
424483
#[test]
425484
fn convert_if_let_result_inside_let() {
426485
check_assist(

crates/ide-assists/src/utils.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1150,3 +1150,19 @@ pub fn is_body_const(sema: &Semantics<'_, RootDatabase>, expr: &ast::Expr) -> bo
11501150
});
11511151
is_const
11521152
}
1153+
1154+
// FIXME: #20460 When hir-ty can analyze the `never` statement at the end of block, remove it
1155+
pub(crate) fn is_never_block(
1156+
sema: &Semantics<'_, RootDatabase>,
1157+
block_expr: &ast::BlockExpr,
1158+
) -> bool {
1159+
if let Some(tail_expr) = block_expr.tail_expr() {
1160+
sema.type_of_expr(&tail_expr).is_some_and(|ty| ty.original.is_never())
1161+
} else if let Some(ast::Stmt::ExprStmt(expr_stmt)) = block_expr.statements().last()
1162+
&& let Some(expr) = expr_stmt.expr()
1163+
{
1164+
sema.type_of_expr(&expr).is_some_and(|ty| ty.original.is_never())
1165+
} else {
1166+
false
1167+
}
1168+
}

0 commit comments

Comments
 (0)