|
1 | 1 | use std::iter::once;
|
2 | 2 |
|
3 |
| -use ide_db::syntax_helpers::node_ext::{is_pattern_cond, single_let}; |
| 3 | +use ide_db::{ |
| 4 | + syntax_helpers::node_ext::{is_pattern_cond, single_let}, |
| 5 | + ty_filter::TryEnum, |
| 6 | +}; |
4 | 7 | use syntax::{
|
5 | 8 | ast::{
|
6 | 9 | self,
|
@@ -41,7 +44,20 @@ use crate::{
|
41 | 44 | // }
|
42 | 45 | // ```
|
43 | 46 | pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
|
44 |
| - let if_expr: ast::IfExpr = ctx.find_node_at_offset()?; |
| 47 | + if let Some(let_stmt) = ctx.find_node_at_offset() { |
| 48 | + let_stmt_to_guarded_return(let_stmt, acc, ctx) |
| 49 | + } else if let Some(if_expr) = ctx.find_node_at_offset() { |
| 50 | + if_expr_to_guarded_return(if_expr, acc, ctx) |
| 51 | + } else { |
| 52 | + None |
| 53 | + } |
| 54 | +} |
| 55 | + |
| 56 | +fn if_expr_to_guarded_return( |
| 57 | + if_expr: ast::IfExpr, |
| 58 | + acc: &mut Assists, |
| 59 | + _ctx: &AssistContext<'_>, |
| 60 | +) -> Option<()> { |
45 | 61 | if if_expr.else_branch().is_some() {
|
46 | 62 | return None;
|
47 | 63 | }
|
@@ -148,6 +164,56 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext<'
|
148 | 164 | )
|
149 | 165 | }
|
150 | 166 |
|
| 167 | +fn let_stmt_to_guarded_return( |
| 168 | + let_stmt: ast::LetStmt, |
| 169 | + acc: &mut Assists, |
| 170 | + ctx: &AssistContext<'_>, |
| 171 | +) -> Option<()> { |
| 172 | + let pat = let_stmt.pat()?; |
| 173 | + let expr = let_stmt.initializer()?; |
| 174 | + |
| 175 | + let try_enum = |
| 176 | + ctx.sema.type_of_expr(&expr).and_then(|ty| TryEnum::from_ty(&ctx.sema, &ty.adjusted()))?; |
| 177 | + |
| 178 | + let happy_pattern = try_enum.happy_pattern(pat); |
| 179 | + let target = let_stmt.syntax().text_range(); |
| 180 | + |
| 181 | + let early_expression: ast::Expr = { |
| 182 | + let parent_block = |
| 183 | + let_stmt.syntax().parent()?.ancestors().find_map(ast::BlockExpr::cast)?; |
| 184 | + let parent_container = parent_block.syntax().parent()?; |
| 185 | + |
| 186 | + match parent_container.kind() { |
| 187 | + WHILE_EXPR | LOOP_EXPR | FOR_EXPR => make::expr_continue(None), |
| 188 | + FN => make::expr_return(None), |
| 189 | + _ => return None, |
| 190 | + } |
| 191 | + }; |
| 192 | + |
| 193 | + acc.add( |
| 194 | + AssistId("convert_to_guarded_return", AssistKind::RefactorRewrite), |
| 195 | + "Convert to guarded return", |
| 196 | + target, |
| 197 | + |edit| { |
| 198 | + let let_stmt = edit.make_mut(let_stmt); |
| 199 | + let let_indent_level = IndentLevel::from_node(let_stmt.syntax()); |
| 200 | + |
| 201 | + let replacement = { |
| 202 | + let let_else_stmt = make::let_else_stmt( |
| 203 | + happy_pattern, |
| 204 | + let_stmt.ty(), |
| 205 | + expr, |
| 206 | + ast::make::tail_only_block_expr(early_expression), |
| 207 | + ); |
| 208 | + let let_else_stmt = let_else_stmt.indent(let_indent_level); |
| 209 | + let_else_stmt.syntax().clone_for_update() |
| 210 | + }; |
| 211 | + |
| 212 | + ted::replace(let_stmt.syntax(), replacement) |
| 213 | + }, |
| 214 | + ) |
| 215 | +} |
| 216 | + |
151 | 217 | #[cfg(test)]
|
152 | 218 | mod tests {
|
153 | 219 | use crate::tests::{check_assist, check_assist_not_applicable};
|
@@ -450,6 +516,62 @@ fn main() {
|
450 | 516 | );
|
451 | 517 | }
|
452 | 518 |
|
| 519 | + #[test] |
| 520 | + fn convert_let_stmt_inside_fn() { |
| 521 | + check_assist( |
| 522 | + convert_to_guarded_return, |
| 523 | + r#" |
| 524 | +//- minicore: option |
| 525 | +fn foo() -> Option<i32> { |
| 526 | + None |
| 527 | +} |
| 528 | +
|
| 529 | +fn main() { |
| 530 | + let x$0 = foo(); |
| 531 | +} |
| 532 | +"#, |
| 533 | + r#" |
| 534 | +fn foo() -> Option<i32> { |
| 535 | + None |
| 536 | +} |
| 537 | +
|
| 538 | +fn main() { |
| 539 | + let Some(x) = foo() else { return }; |
| 540 | +} |
| 541 | +"#, |
| 542 | + ); |
| 543 | + } |
| 544 | + |
| 545 | + #[test] |
| 546 | + fn convert_let_stmt_inside_loop() { |
| 547 | + check_assist( |
| 548 | + convert_to_guarded_return, |
| 549 | + r#" |
| 550 | +//- minicore: option |
| 551 | +fn foo() -> Option<i32> { |
| 552 | + None |
| 553 | +} |
| 554 | +
|
| 555 | +fn main() { |
| 556 | + loop { |
| 557 | + let x$0 = foo(); |
| 558 | + } |
| 559 | +} |
| 560 | +"#, |
| 561 | + r#" |
| 562 | +fn foo() -> Option<i32> { |
| 563 | + None |
| 564 | +} |
| 565 | +
|
| 566 | +fn main() { |
| 567 | + loop { |
| 568 | + let Some(x) = foo() else { continue }; |
| 569 | + } |
| 570 | +} |
| 571 | +"#, |
| 572 | + ); |
| 573 | + } |
| 574 | + |
453 | 575 | #[test]
|
454 | 576 | fn convert_arbitrary_if_let_patterns() {
|
455 | 577 | check_assist(
|
|
0 commit comments