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