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