]> git.lizzy.rs Git - rust.git/blob - src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs
Rollup merge of #101420 - kraktus:doc_hir_local, r=cjgillot
[rust.git] / src / tools / rust-analyzer / crates / ide-assists / src / handlers / replace_turbofish_with_explicit_type.rs
1 use syntax::{
2     ast::{Expr, GenericArg},
3     ast::{LetStmt, Type::InferType},
4     AstNode, TextRange,
5 };
6
7 use crate::{
8     assist_context::{AssistContext, Assists},
9     AssistId, AssistKind,
10 };
11
12 // Assist: replace_turbofish_with_explicit_type
13 //
14 // Converts `::<_>` to an explicit type assignment.
15 //
16 // ```
17 // fn make<T>() -> T { ) }
18 // fn main() {
19 //     let a = make$0::<i32>();
20 // }
21 // ```
22 // ->
23 // ```
24 // fn make<T>() -> T { ) }
25 // fn main() {
26 //     let a: i32 = make();
27 // }
28 // ```
29 pub(crate) fn replace_turbofish_with_explicit_type(
30     acc: &mut Assists,
31     ctx: &AssistContext<'_>,
32 ) -> Option<()> {
33     let let_stmt = ctx.find_node_at_offset::<LetStmt>()?;
34
35     let initializer = let_stmt.initializer()?;
36
37     let generic_args = match &initializer {
38         Expr::MethodCallExpr(ce) => ce.generic_arg_list()?,
39         Expr::CallExpr(ce) => {
40             if let Expr::PathExpr(pe) = ce.expr()? {
41                 pe.path()?.segment()?.generic_arg_list()?
42             } else {
43                 cov_mark::hit!(not_applicable_if_non_path_function_call);
44                 return None;
45             }
46         }
47         _ => {
48             cov_mark::hit!(not_applicable_if_non_function_call_initializer);
49             return None;
50         }
51     };
52
53     // Find range of ::<_>
54     let colon2 = generic_args.coloncolon_token()?;
55     let r_angle = generic_args.r_angle_token()?;
56     let turbofish_range = TextRange::new(colon2.text_range().start(), r_angle.text_range().end());
57
58     let turbofish_args: Vec<GenericArg> = generic_args.generic_args().into_iter().collect();
59
60     // Find type of ::<_>
61     if turbofish_args.len() != 1 {
62         cov_mark::hit!(not_applicable_if_not_single_arg);
63         return None;
64     }
65
66     // An improvement would be to check that this is correctly part of the return value of the
67     // function call, or sub in the actual return type.
68     let turbofish_type = &turbofish_args[0];
69
70     let initializer_start = initializer.syntax().text_range().start();
71     if ctx.offset() > turbofish_range.end() || ctx.offset() < initializer_start {
72         cov_mark::hit!(not_applicable_outside_turbofish);
73         return None;
74     }
75
76     if let None = let_stmt.colon_token() {
77         // If there's no colon in a let statement, then there is no explicit type.
78         // let x = fn::<...>();
79         let ident_range = let_stmt.pat()?.syntax().text_range();
80
81         return acc.add(
82             AssistId("replace_turbofish_with_explicit_type", AssistKind::RefactorRewrite),
83             "Replace turbofish with explicit type",
84             TextRange::new(initializer_start, turbofish_range.end()),
85             |builder| {
86                 builder.insert(ident_range.end(), format!(": {}", turbofish_type));
87                 builder.delete(turbofish_range);
88             },
89         );
90     } else if let Some(InferType(t)) = let_stmt.ty() {
91         // If there's a type inference underscore, we can offer to replace it with the type in
92         // the turbofish.
93         // let x: _ = fn::<...>();
94         let underscore_range = t.syntax().text_range();
95
96         return acc.add(
97             AssistId("replace_turbofish_with_explicit_type", AssistKind::RefactorRewrite),
98             "Replace `_` with turbofish type",
99             turbofish_range,
100             |builder| {
101                 builder.replace(underscore_range, turbofish_type.to_string());
102                 builder.delete(turbofish_range);
103             },
104         );
105     }
106
107     None
108 }
109
110 #[cfg(test)]
111 mod tests {
112     use super::*;
113
114     use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
115
116     #[test]
117     fn replaces_turbofish_for_vec_string() {
118         check_assist(
119             replace_turbofish_with_explicit_type,
120             r#"
121 fn make<T>() -> T {}
122 fn main() {
123     let a = make$0::<Vec<String>>();
124 }
125 "#,
126             r#"
127 fn make<T>() -> T {}
128 fn main() {
129     let a: Vec<String> = make();
130 }
131 "#,
132         );
133     }
134
135     #[test]
136     fn replaces_method_calls() {
137         // foo.make() is a method call which uses a different expr in the let initializer
138         check_assist(
139             replace_turbofish_with_explicit_type,
140             r#"
141 fn make<T>() -> T {}
142 fn main() {
143     let a = foo.make$0::<Vec<String>>();
144 }
145 "#,
146             r#"
147 fn make<T>() -> T {}
148 fn main() {
149     let a: Vec<String> = foo.make();
150 }
151 "#,
152         );
153     }
154
155     #[test]
156     fn replace_turbofish_target() {
157         check_assist_target(
158             replace_turbofish_with_explicit_type,
159             r#"
160 fn make<T>() -> T {}
161 fn main() {
162     let a = $0make::<Vec<String>>();
163 }
164 "#,
165             r#"make::<Vec<String>>"#,
166         );
167     }
168
169     #[test]
170     fn not_applicable_outside_turbofish() {
171         cov_mark::check!(not_applicable_outside_turbofish);
172         check_assist_not_applicable(
173             replace_turbofish_with_explicit_type,
174             r#"
175 fn make<T>() -> T {}
176 fn main() {
177     let $0a = make::<Vec<String>>();
178 }
179 "#,
180         );
181     }
182
183     #[test]
184     fn replace_inferred_type_placeholder() {
185         check_assist(
186             replace_turbofish_with_explicit_type,
187             r#"
188 fn make<T>() -> T {}
189 fn main() {
190     let a: _ = make$0::<Vec<String>>();
191 }
192 "#,
193             r#"
194 fn make<T>() -> T {}
195 fn main() {
196     let a: Vec<String> = make();
197 }
198 "#,
199         );
200     }
201
202     #[test]
203     fn not_applicable_constant_initializer() {
204         cov_mark::check!(not_applicable_if_non_function_call_initializer);
205         check_assist_not_applicable(
206             replace_turbofish_with_explicit_type,
207             r#"
208 fn make<T>() -> T {}
209 fn main() {
210     let a = "foo"$0;
211 }
212 "#,
213         );
214     }
215
216     #[test]
217     fn not_applicable_non_path_function_call() {
218         cov_mark::check!(not_applicable_if_non_path_function_call);
219         check_assist_not_applicable(
220             replace_turbofish_with_explicit_type,
221             r#"
222 fn make<T>() -> T {}
223 fn main() {
224     $0let a = (|| {})();
225 }
226 "#,
227         );
228     }
229
230     #[test]
231     fn non_applicable_multiple_generic_args() {
232         cov_mark::check!(not_applicable_if_not_single_arg);
233         check_assist_not_applicable(
234             replace_turbofish_with_explicit_type,
235             r#"
236 fn make<T>() -> T {}
237 fn main() {
238     let a = make$0::<Vec<String>, i32>();
239 }
240 "#,
241         );
242     }
243 }