rust/crates/ide-assists/src/handlers/replace_try_expr_with_match.rs
2022-07-20 15:06:15 +02:00

151 lines
3.8 KiB
Rust

use std::iter;
use ide_db::{
assists::{AssistId, AssistKind},
ty_filter::TryEnum,
};
use syntax::{
ast::{
self,
edit::{AstNodeEdit, IndentLevel},
make,
},
AstNode, T,
};
use crate::assist_context::{AssistContext, Assists};
// Assist: replace_try_expr_with_match
//
// Replaces a `try` expression with a `match` expression.
//
// ```
// # //- minicore:option
// fn handle() {
// let pat = Some(true)$0?;
// }
// ```
// ->
// ```
// fn handle() {
// let pat = match Some(true) {
// Some(it) => it,
// None => return None,
// };
// }
// ```
pub(crate) fn replace_try_expr_with_match(
acc: &mut Assists,
ctx: &AssistContext<'_>,
) -> Option<()> {
let qm_kw = ctx.find_token_syntax_at_offset(T![?])?;
let qm_kw_parent = qm_kw.parent().and_then(ast::TryExpr::cast)?;
let expr = qm_kw_parent.expr()?;
let expr_type_info = ctx.sema.type_of_expr(&expr)?;
let try_enum = TryEnum::from_ty(&ctx.sema, &expr_type_info.original)?;
let target = qm_kw_parent.syntax().text_range();
acc.add(
AssistId("replace_try_expr_with_match", AssistKind::RefactorRewrite),
"Replace try expression with match",
target,
|edit| {
let sad_pat = match try_enum {
TryEnum::Option => make::path_pat(make::ext::ident_path("None")),
TryEnum::Result => make::tuple_struct_pat(
make::ext::ident_path("Err"),
iter::once(make::path_pat(make::ext::ident_path("err"))),
)
.into(),
};
let sad_expr = match try_enum {
TryEnum::Option => {
make::expr_return(Some(make::expr_path(make::ext::ident_path("None"))))
}
TryEnum::Result => make::expr_return(Some(make::expr_call(
make::expr_path(make::ext::ident_path("Err")),
make::arg_list(iter::once(make::expr_path(make::ext::ident_path("err")))),
))),
};
let happy_arm = make::match_arm(
iter::once(
try_enum.happy_pattern(make::ident_pat(false, false, make::name("it")).into()),
),
None,
make::expr_path(make::ext::ident_path("it")),
);
let sad_arm = make::match_arm(iter::once(sad_pat), None, sad_expr);
let match_arm_list = make::match_arm_list([happy_arm, sad_arm]);
let expr_match = make::expr_match(expr, match_arm_list)
.indent(IndentLevel::from_node(qm_kw_parent.syntax()));
edit.replace_ast::<ast::Expr>(qm_kw_parent.into(), expr_match);
},
)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::tests::{check_assist, check_assist_not_applicable};
#[test]
fn test_replace_try_expr_with_match_not_applicable() {
check_assist_not_applicable(
replace_try_expr_with_match,
r#"
fn test() {
let pat: u32 = 25$0;
}
"#,
);
}
#[test]
fn test_replace_try_expr_with_match_option() {
check_assist(
replace_try_expr_with_match,
r#"
//- minicore:option
fn test() {
let pat = Some(true)$0?;
}
"#,
r#"
fn test() {
let pat = match Some(true) {
Some(it) => it,
None => return None,
};
}
"#,
);
}
#[test]
fn test_replace_try_expr_with_match_result() {
check_assist(
replace_try_expr_with_match,
r#"
//- minicore:result
fn test() {
let pat = Ok(true)$0?;
}
"#,
r#"
fn test() {
let pat = match Ok(true) {
Ok(it) => it,
Err(err) => return Err(err),
};
}
"#,
);
}
}