]> git.lizzy.rs Git - rust.git/blob - src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_tuple.rs
Auto merge of #103913 - Neutron3529:patch-1, r=thomcc
[rust.git] / src / tools / rust-analyzer / crates / ide-assists / src / handlers / unwrap_tuple.rs
1 use syntax::{
2     ast::{self, edit::AstNodeEdit},
3     AstNode, T,
4 };
5
6 use crate::{AssistContext, AssistId, AssistKind, Assists};
7
8 // Assist: unwrap_tuple
9 //
10 // Unwrap the tuple to different variables.
11 //
12 // ```
13 // # //- minicore: result
14 // fn main() {
15 //     $0let (foo, bar) = ("Foo", "Bar");
16 // }
17 // ```
18 // ->
19 // ```
20 // fn main() {
21 //     let foo = "Foo";
22 //     let bar = "Bar";
23 // }
24 // ```
25 pub(crate) fn unwrap_tuple(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
26     let let_kw = ctx.find_token_syntax_at_offset(T![let])?;
27     let let_stmt = let_kw.parent().and_then(ast::LetStmt::cast)?;
28     let indent_level = let_stmt.indent_level().0 as usize;
29     let pat = let_stmt.pat()?;
30     let ty = let_stmt.ty();
31     let init = let_stmt.initializer()?;
32
33     // This only applies for tuple patterns, types, and initializers.
34     let tuple_pat = match pat {
35         ast::Pat::TuplePat(pat) => pat,
36         _ => return None,
37     };
38     let tuple_ty = ty.and_then(|it| match it {
39         ast::Type::TupleType(ty) => Some(ty),
40         _ => None,
41     });
42     let tuple_init = match init {
43         ast::Expr::TupleExpr(expr) => expr,
44         _ => return None,
45     };
46
47     if tuple_pat.fields().count() != tuple_init.fields().count() {
48         return None;
49     }
50     if let Some(tys) = &tuple_ty {
51         if tuple_pat.fields().count() != tys.fields().count() {
52             return None;
53         }
54     }
55
56     let parent = let_kw.parent()?;
57
58     acc.add(
59         AssistId("unwrap_tuple", AssistKind::RefactorRewrite),
60         "Unwrap tuple",
61         let_kw.text_range(),
62         |edit| {
63             let indents = "    ".repeat(indent_level);
64
65             // If there is an ascribed type, insert that type for each declaration,
66             // otherwise, omit that type.
67             if let Some(tys) = tuple_ty {
68                 let mut zipped_decls = String::new();
69                 for (pat, ty, expr) in
70                     itertools::izip!(tuple_pat.fields(), tys.fields(), tuple_init.fields())
71                 {
72                     zipped_decls.push_str(&format!("{indents}let {pat}: {ty} = {expr};\n"))
73                 }
74                 edit.replace(parent.text_range(), zipped_decls.trim());
75             } else {
76                 let mut zipped_decls = String::new();
77                 for (pat, expr) in itertools::izip!(tuple_pat.fields(), tuple_init.fields()) {
78                     zipped_decls.push_str(&format!("{indents}let {pat} = {expr};\n"));
79                 }
80                 edit.replace(parent.text_range(), zipped_decls.trim());
81             }
82         },
83     )
84 }
85
86 #[cfg(test)]
87 mod tests {
88     use crate::tests::check_assist;
89
90     use super::*;
91
92     #[test]
93     fn unwrap_tuples() {
94         check_assist(
95             unwrap_tuple,
96             r#"
97 fn main() {
98     $0let (foo, bar) = ("Foo", "Bar");
99 }
100 "#,
101             r#"
102 fn main() {
103     let foo = "Foo";
104     let bar = "Bar";
105 }
106 "#,
107         );
108
109         check_assist(
110             unwrap_tuple,
111             r#"
112 fn main() {
113     $0let (foo, bar, baz) = ("Foo", "Bar", "Baz");
114 }
115 "#,
116             r#"
117 fn main() {
118     let foo = "Foo";
119     let bar = "Bar";
120     let baz = "Baz";
121 }
122 "#,
123         );
124     }
125
126     #[test]
127     fn unwrap_tuple_with_types() {
128         check_assist(
129             unwrap_tuple,
130             r#"
131 fn main() {
132     $0let (foo, bar): (u8, i32) = (5, 10);
133 }
134 "#,
135             r#"
136 fn main() {
137     let foo: u8 = 5;
138     let bar: i32 = 10;
139 }
140 "#,
141         );
142
143         check_assist(
144             unwrap_tuple,
145             r#"
146 fn main() {
147     $0let (foo, bar, baz): (u8, i32, f64) = (5, 10, 17.5);
148 }
149 "#,
150             r#"
151 fn main() {
152     let foo: u8 = 5;
153     let bar: i32 = 10;
154     let baz: f64 = 17.5;
155 }
156 "#,
157         );
158     }
159 }