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);
}
```
This commit is contained in:
parent
7de38d36eb
commit
083d279d2e
2 changed files with 90 additions and 15 deletions
|
|
@ -17,7 +17,7 @@ use syntax::{
|
|||
use crate::{
|
||||
AssistId,
|
||||
assist_context::{AssistContext, Assists},
|
||||
utils::invert_boolean_expression_legacy,
|
||||
utils::{invert_boolean_expression_legacy, is_never_block},
|
||||
};
|
||||
|
||||
// Assist: convert_to_guarded_return
|
||||
|
|
@ -54,9 +54,13 @@ fn if_expr_to_guarded_return(
|
|||
acc: &mut Assists,
|
||||
ctx: &AssistContext<'_>,
|
||||
) -> Option<()> {
|
||||
if if_expr.else_branch().is_some() {
|
||||
return None;
|
||||
}
|
||||
let else_block = match if_expr.else_branch() {
|
||||
Some(ast::ElseBranch::Block(block_expr)) if is_never_block(&ctx.sema, &block_expr) => {
|
||||
Some(block_expr)
|
||||
}
|
||||
Some(_) => return None,
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let cond = if_expr.condition()?;
|
||||
|
||||
|
|
@ -96,7 +100,11 @@ fn if_expr_to_guarded_return(
|
|||
|
||||
let parent_container = parent_block.syntax().parent()?;
|
||||
|
||||
let early_expression: ast::Expr = early_expression(parent_container, &ctx.sema)?;
|
||||
let early_expression = else_block
|
||||
.or_else(|| {
|
||||
early_expression(parent_container, &ctx.sema).map(ast::make::tail_only_block_expr)
|
||||
})?
|
||||
.reset_indent();
|
||||
|
||||
then_block.syntax().first_child_or_token().map(|t| t.kind() == T!['{'])?;
|
||||
|
||||
|
|
@ -123,21 +131,14 @@ fn if_expr_to_guarded_return(
|
|||
&& let (Some(pat), Some(expr)) = (let_expr.pat(), let_expr.expr())
|
||||
{
|
||||
// If-let.
|
||||
let let_else_stmt = make::let_else_stmt(
|
||||
pat,
|
||||
None,
|
||||
expr,
|
||||
ast::make::tail_only_block_expr(early_expression.clone()),
|
||||
);
|
||||
let let_else_stmt =
|
||||
make::let_else_stmt(pat, None, expr, early_expression.clone());
|
||||
let let_else_stmt = let_else_stmt.indent(if_indent_level);
|
||||
let_else_stmt.syntax().clone()
|
||||
} else {
|
||||
// If.
|
||||
let new_expr = {
|
||||
let then_branch = make::block_expr(
|
||||
once(make::expr_stmt(early_expression.clone()).into()),
|
||||
None,
|
||||
);
|
||||
let then_branch = clean_stmt_block(&early_expression);
|
||||
let cond = invert_boolean_expression_legacy(expr);
|
||||
make::expr_if(cond, then_branch, None).indent(if_indent_level)
|
||||
};
|
||||
|
|
@ -272,6 +273,17 @@ fn flat_let_chain(mut expr: ast::Expr) -> Vec<ast::Expr> {
|
|||
chains
|
||||
}
|
||||
|
||||
fn clean_stmt_block(block: &ast::BlockExpr) -> ast::BlockExpr {
|
||||
if block.statements().next().is_none()
|
||||
&& let Some(tail_expr) = block.tail_expr()
|
||||
&& block.modifier().is_none()
|
||||
{
|
||||
make::block_expr(once(make::expr_stmt(tail_expr).into()), None)
|
||||
} else {
|
||||
block.clone()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::tests::{check_assist, check_assist_not_applicable};
|
||||
|
|
@ -421,6 +433,53 @@ fn main() {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn convert_if_let_has_never_type_else_block() {
|
||||
check_assist(
|
||||
convert_to_guarded_return,
|
||||
r#"
|
||||
fn main() {
|
||||
if$0 let Ok(x) = Err(92) {
|
||||
foo(x);
|
||||
} else {
|
||||
// needless comment
|
||||
return;
|
||||
}
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
fn main() {
|
||||
let Ok(x) = Err(92) else {
|
||||
// needless comment
|
||||
return;
|
||||
};
|
||||
foo(x);
|
||||
}
|
||||
"#,
|
||||
);
|
||||
|
||||
check_assist(
|
||||
convert_to_guarded_return,
|
||||
r#"
|
||||
fn main() {
|
||||
if$0 let Ok(x) = Err(92) {
|
||||
foo(x);
|
||||
} else {
|
||||
return
|
||||
}
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
fn main() {
|
||||
let Ok(x) = Err(92) else {
|
||||
return
|
||||
};
|
||||
foo(x);
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn convert_if_let_result_inside_let() {
|
||||
check_assist(
|
||||
|
|
|
|||
|
|
@ -1150,3 +1150,19 @@ pub fn is_body_const(sema: &Semantics<'_, RootDatabase>, expr: &ast::Expr) -> bo
|
|||
});
|
||||
is_const
|
||||
}
|
||||
|
||||
// FIXME: #20460 When hir-ty can analyze the `never` statement at the end of block, remove it
|
||||
pub(crate) fn is_never_block(
|
||||
sema: &Semantics<'_, RootDatabase>,
|
||||
block_expr: &ast::BlockExpr,
|
||||
) -> bool {
|
||||
if let Some(tail_expr) = block_expr.tail_expr() {
|
||||
sema.type_of_expr(&tail_expr).is_some_and(|ty| ty.original.is_never())
|
||||
} else if let Some(ast::Stmt::ExprStmt(expr_stmt)) = block_expr.statements().last()
|
||||
&& let Some(expr) = expr_stmt.expr()
|
||||
{
|
||||
sema.type_of_expr(&expr).is_some_and(|ty| ty.original.is_never())
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue