]> git.lizzy.rs Git - rust.git/blob - crates/ide_assists/src/handlers/replace_try_expr_with_match.rs
Merge #10479
[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_arms = [happy_arm, sad_arm];
80             let match_arm_list = make::match_arm_list(std::array::IntoIter::new(match_arms));
81
82             let expr_match = make::expr_match(expr, match_arm_list)
83                 .indent(IndentLevel::from_node(qm_kw_parent.syntax()));
84             edit.replace_ast::<ast::Expr>(qm_kw_parent.into(), expr_match);
85         },
86     )
87 }
88
89 #[cfg(test)]
90 mod tests {
91     use super::*;
92
93     use crate::tests::{check_assist, check_assist_not_applicable};
94
95     #[test]
96     fn test_replace_try_expr_with_match_not_applicable() {
97         check_assist_not_applicable(
98             replace_try_expr_with_match,
99             r#"
100                 fn test() {
101                     let pat: u32 = 25$0;
102                 }
103             "#,
104         );
105     }
106
107     #[test]
108     fn test_replace_try_expr_with_match_option() {
109         check_assist(
110             replace_try_expr_with_match,
111             r#"
112 //- minicore:option
113 fn test() {
114     let pat = Some(true)$0?;
115 }
116             "#,
117             r#"
118 fn test() {
119     let pat = match Some(true) {
120         Some(it) => it,
121         None => return None,
122     };
123 }
124             "#,
125         );
126     }
127
128     #[test]
129     fn test_replace_try_expr_with_match_result() {
130         check_assist(
131             replace_try_expr_with_match,
132             r#"
133 //- minicore:result
134 fn test() {
135     let pat = Ok(true)$0?;
136 }
137             "#,
138             r#"
139 fn test() {
140     let pat = match Ok(true) {
141         Ok(it) => it,
142         Err(err) => return Err(err),
143     };
144 }
145             "#,
146         );
147     }
148 }