]> git.lizzy.rs Git - rust.git/blob - crates/ide_assists/src/handlers/replace_try_expr_with_match.rs
add sourcegen
[rust.git] / crates / ide_assists / src / handlers / replace_try_expr_with_match.rs
1 use std::iter;
2
3 use ide_db::{
4     assists::{AssistId, AssistKind},
5     ty_filter::TryEnum,
6 };
7 use syntax::{
8     ast::{
9         self,
10         edit::{AstNodeEdit, IndentLevel},
11         make,
12     },
13     AstNode, T,
14 };
15
16 use crate::assist_context::{AssistContext, Assists};
17
18 // Assist: replace_try_expr_with_match
19 //
20 // Replaces a `try` expression with a `match` expression.
21 //
22 // ```
23 // let pat = Some(true)$0?;
24 // ```
25 // ->
26 // ```
27 // let pat = match Some(true) {
28 //     Some(it) => it,
29 //     None => return None,
30 // };
31 // ```
32 pub(crate) fn replace_try_expr_with_match(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
33     let qm_kw = ctx.find_token_syntax_at_offset(T![?])?;
34     let qm_kw_parent = qm_kw.parent().and_then(ast::TryExpr::cast)?;
35
36     let expr = qm_kw_parent.expr()?;
37     let expr_type_info = ctx.sema.type_of_expr(&expr)?;
38
39     let try_enum = TryEnum::from_ty(&ctx.sema, &expr_type_info.original)?;
40
41     let target = qm_kw_parent.syntax().text_range();
42     acc.add(
43         AssistId("replace_try_expr_with_match", AssistKind::RefactorRewrite),
44         "Replace try expression with match",
45         target,
46         |edit| {
47             let sad_pat = match try_enum {
48                 TryEnum::Option => make::path_pat(make::ext::ident_path("None")),
49                 TryEnum::Result => make::tuple_struct_pat(
50                     make::ext::ident_path("Err"),
51                     iter::once(make::path_pat(make::ext::ident_path("err"))),
52                 )
53                 .into(),
54             };
55             let sad_expr = match try_enum {
56                 TryEnum::Option => {
57                     make::expr_return(Some(make::expr_path(make::ext::ident_path("None"))))
58                 }
59                 TryEnum::Result => make::expr_return(Some(make::expr_call(
60                     make::expr_path(make::ext::ident_path("Err")),
61                     make::arg_list(iter::once(make::expr_path(make::ext::ident_path("err")))),
62                 ))),
63             };
64
65             let happy_arm = make::match_arm(
66                 iter::once(
67                     try_enum.happy_pattern(make::ident_pat(false, false, make::name("it")).into()),
68                 ),
69                 None,
70                 make::expr_path(make::ext::ident_path("it")),
71             );
72             let sad_arm = make::match_arm(iter::once(sad_pat), None, sad_expr);
73
74             let match_arms = [happy_arm, sad_arm];
75             let match_arm_list = make::match_arm_list(std::array::IntoIter::new(match_arms));
76
77             let expr_match = make::expr_match(expr, match_arm_list)
78                 .indent(IndentLevel::from_node(qm_kw_parent.syntax()));
79             edit.replace_ast::<ast::Expr>(qm_kw_parent.into(), expr_match);
80         },
81     )
82 }
83
84 #[cfg(test)]
85 mod tests {
86     use super::*;
87
88     use crate::tests::{check_assist, check_assist_not_applicable};
89
90     #[test]
91     fn test_replace_try_expr_with_match_not_applicable() {
92         check_assist_not_applicable(
93             replace_try_expr_with_match,
94             r#"
95                 fn test() {
96                     let pat = 25$0;
97                 }
98             "#,
99         );
100     }
101
102     #[test]
103     fn test_replace_try_expr_with_match_option() {
104         check_assist(
105             replace_try_expr_with_match,
106             r#"
107 //- minicore:option
108 fn test() {
109     let pat = Some(true)$0?;
110 }
111             "#,
112             r#"
113 fn test() {
114     let pat = match Some(true) {
115         Some(it) => it,
116         None => return None,
117     };
118 }
119             "#,
120         );
121     }
122
123     #[test]
124     fn test_replace_try_expr_with_match_result() {
125         check_assist(
126             replace_try_expr_with_match,
127             r#"
128 //- minicore:result
129 fn test() {
130     let pat = Ok(true)$0?;
131 }
132             "#,
133             r#"
134 fn test() {
135     let pat = match Ok(true) {
136         Ok(it) => it,
137         Err(err) => return Err(err),
138     };
139 }
140             "#,
141         );
142     }
143 }