]> git.lizzy.rs Git - rust.git/blob - src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs
Auto merge of #103913 - Neutron3529:patch-1, r=thomcc
[rust.git] / src / tools / rust-analyzer / crates / ide-assists / src / handlers / replace_turbofish_with_explicit_type.rs
1 use hir::HirDisplay;
2 use syntax::{
3     ast::{Expr, GenericArg, GenericArgList},
4     ast::{LetStmt, Type::InferType},
5     AstNode, TextRange,
6 };
7
8 use crate::{
9     assist_context::{AssistContext, Assists},
10     AssistId, AssistKind,
11 };
12
13 // Assist: replace_turbofish_with_explicit_type
14 //
15 // Converts `::<_>` to an explicit type assignment.
16 //
17 // ```
18 // fn make<T>() -> T { ) }
19 // fn main() {
20 //     let a = make$0::<i32>();
21 // }
22 // ```
23 // ->
24 // ```
25 // fn make<T>() -> T { ) }
26 // fn main() {
27 //     let a: i32 = make();
28 // }
29 // ```
30 pub(crate) fn replace_turbofish_with_explicit_type(
31     acc: &mut Assists,
32     ctx: &AssistContext<'_>,
33 ) -> Option<()> {
34     let let_stmt = ctx.find_node_at_offset::<LetStmt>()?;
35
36     let initializer = let_stmt.initializer()?;
37
38     let generic_args = generic_arg_list(&initializer)?;
39
40     // Find range of ::<_>
41     let colon2 = generic_args.coloncolon_token()?;
42     let r_angle = generic_args.r_angle_token()?;
43     let turbofish_range = TextRange::new(colon2.text_range().start(), r_angle.text_range().end());
44
45     let turbofish_args: Vec<GenericArg> = generic_args.generic_args().into_iter().collect();
46
47     // Find type of ::<_>
48     if turbofish_args.len() != 1 {
49         cov_mark::hit!(not_applicable_if_not_single_arg);
50         return None;
51     }
52
53     // An improvement would be to check that this is correctly part of the return value of the
54     // function call, or sub in the actual return type.
55     let returned_type = match ctx.sema.type_of_expr(&initializer) {
56         Some(returned_type) if !returned_type.original.contains_unknown() => {
57             let module = ctx.sema.scope(let_stmt.syntax())?.module();
58             returned_type.original.display_source_code(ctx.db(), module.into()).ok()?
59         }
60         _ => {
61             cov_mark::hit!(fallback_to_turbofish_type_if_type_info_not_available);
62             turbofish_args[0].to_string()
63         }
64     };
65
66     let initializer_start = initializer.syntax().text_range().start();
67     if ctx.offset() > turbofish_range.end() || ctx.offset() < initializer_start {
68         cov_mark::hit!(not_applicable_outside_turbofish);
69         return None;
70     }
71
72     if let None = let_stmt.colon_token() {
73         // If there's no colon in a let statement, then there is no explicit type.
74         // let x = fn::<...>();
75         let ident_range = let_stmt.pat()?.syntax().text_range();
76
77         return acc.add(
78             AssistId("replace_turbofish_with_explicit_type", AssistKind::RefactorRewrite),
79             "Replace turbofish with explicit type",
80             TextRange::new(initializer_start, turbofish_range.end()),
81             |builder| {
82                 builder.insert(ident_range.end(), format!(": {returned_type}"));
83                 builder.delete(turbofish_range);
84             },
85         );
86     } else if let Some(InferType(t)) = let_stmt.ty() {
87         // If there's a type inference underscore, we can offer to replace it with the type in
88         // the turbofish.
89         // let x: _ = fn::<...>();
90         let underscore_range = t.syntax().text_range();
91
92         return acc.add(
93             AssistId("replace_turbofish_with_explicit_type", AssistKind::RefactorRewrite),
94             "Replace `_` with turbofish type",
95             turbofish_range,
96             |builder| {
97                 builder.replace(underscore_range, returned_type);
98                 builder.delete(turbofish_range);
99             },
100         );
101     }
102
103     None
104 }
105
106 fn generic_arg_list(expr: &Expr) -> Option<GenericArgList> {
107     match expr {
108         Expr::MethodCallExpr(expr) => expr.generic_arg_list(),
109         Expr::CallExpr(expr) => {
110             if let Expr::PathExpr(pe) = expr.expr()? {
111                 pe.path()?.segment()?.generic_arg_list()
112             } else {
113                 cov_mark::hit!(not_applicable_if_non_path_function_call);
114                 return None;
115             }
116         }
117         Expr::AwaitExpr(expr) => generic_arg_list(&expr.expr()?),
118         Expr::TryExpr(expr) => generic_arg_list(&expr.expr()?),
119         _ => {
120             cov_mark::hit!(not_applicable_if_non_function_call_initializer);
121             None
122         }
123     }
124 }
125
126 #[cfg(test)]
127 mod tests {
128     use super::*;
129
130     use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
131
132     #[test]
133     fn replaces_turbofish_for_vec_string() {
134         cov_mark::check!(fallback_to_turbofish_type_if_type_info_not_available);
135         check_assist(
136             replace_turbofish_with_explicit_type,
137             r#"
138 fn make<T>() -> T {}
139 fn main() {
140     let a = make$0::<Vec<String>>();
141 }
142 "#,
143             r#"
144 fn make<T>() -> T {}
145 fn main() {
146     let a: Vec<String> = make();
147 }
148 "#,
149         );
150     }
151
152     #[test]
153     fn replaces_method_calls() {
154         // foo.make() is a method call which uses a different expr in the let initializer
155         cov_mark::check!(fallback_to_turbofish_type_if_type_info_not_available);
156         check_assist(
157             replace_turbofish_with_explicit_type,
158             r#"
159 fn make<T>() -> T {}
160 fn main() {
161     let a = foo.make$0::<Vec<String>>();
162 }
163 "#,
164             r#"
165 fn make<T>() -> T {}
166 fn main() {
167     let a: Vec<String> = foo.make();
168 }
169 "#,
170         );
171     }
172
173     #[test]
174     fn replace_turbofish_target() {
175         check_assist_target(
176             replace_turbofish_with_explicit_type,
177             r#"
178 fn make<T>() -> T {}
179 fn main() {
180     let a = $0make::<Vec<String>>();
181 }
182 "#,
183             r#"make::<Vec<String>>"#,
184         );
185     }
186
187     #[test]
188     fn not_applicable_outside_turbofish() {
189         cov_mark::check!(not_applicable_outside_turbofish);
190         check_assist_not_applicable(
191             replace_turbofish_with_explicit_type,
192             r#"
193 fn make<T>() -> T {}
194 fn main() {
195     let $0a = make::<Vec<String>>();
196 }
197 "#,
198         );
199     }
200
201     #[test]
202     fn replace_inferred_type_placeholder() {
203         check_assist(
204             replace_turbofish_with_explicit_type,
205             r#"
206 fn make<T>() -> T {}
207 fn main() {
208     let a: _ = make$0::<Vec<String>>();
209 }
210 "#,
211             r#"
212 fn make<T>() -> T {}
213 fn main() {
214     let a: Vec<String> = make();
215 }
216 "#,
217         );
218     }
219
220     #[test]
221     fn not_applicable_constant_initializer() {
222         cov_mark::check!(not_applicable_if_non_function_call_initializer);
223         check_assist_not_applicable(
224             replace_turbofish_with_explicit_type,
225             r#"
226 fn make<T>() -> T {}
227 fn main() {
228     let a = "foo"$0;
229 }
230 "#,
231         );
232     }
233
234     #[test]
235     fn not_applicable_non_path_function_call() {
236         cov_mark::check!(not_applicable_if_non_path_function_call);
237         check_assist_not_applicable(
238             replace_turbofish_with_explicit_type,
239             r#"
240 fn make<T>() -> T {}
241 fn main() {
242     $0let a = (|| {})();
243 }
244 "#,
245         );
246     }
247
248     #[test]
249     fn non_applicable_multiple_generic_args() {
250         cov_mark::check!(not_applicable_if_not_single_arg);
251         check_assist_not_applicable(
252             replace_turbofish_with_explicit_type,
253             r#"
254 fn make<T>() -> T {}
255 fn main() {
256     let a = make$0::<Vec<String>, i32>();
257 }
258 "#,
259         );
260     }
261
262     #[test]
263     fn replaces_turbofish_for_known_type() {
264         check_assist(
265             replace_turbofish_with_explicit_type,
266             r#"
267 fn make<T>() -> T {}
268 fn main() {
269     let a = make$0::<i32>();
270 }
271 "#,
272             r#"
273 fn make<T>() -> T {}
274 fn main() {
275     let a: i32 = make();
276 }
277 "#,
278         );
279         check_assist(
280             replace_turbofish_with_explicit_type,
281             r#"
282 //- minicore: option
283 fn make<T>() -> T {}
284 fn main() {
285     let a = make$0::<Option<bool>>();
286 }
287 "#,
288             r#"
289 fn make<T>() -> T {}
290 fn main() {
291     let a: Option<bool> = make();
292 }
293 "#,
294         );
295     }
296
297     #[test]
298     fn replaces_turbofish_not_same_type() {
299         check_assist(
300             replace_turbofish_with_explicit_type,
301             r#"
302 //- minicore: option
303 fn make<T>() -> Option<T> {}
304 fn main() {
305     let a = make$0::<u128>();
306 }
307 "#,
308             r#"
309 fn make<T>() -> Option<T> {}
310 fn main() {
311     let a: Option<u128> = make();
312 }
313 "#,
314         );
315     }
316
317     #[test]
318     fn replaces_turbofish_for_type_with_defaulted_generic_param() {
319         check_assist(
320             replace_turbofish_with_explicit_type,
321             r#"
322 struct HasDefault<T, U = i32>(T, U);
323 fn make<T>() -> HasDefault<T> {}
324 fn main() {
325     let a = make$0::<bool>();
326 }
327 "#,
328             r#"
329 struct HasDefault<T, U = i32>(T, U);
330 fn make<T>() -> HasDefault<T> {}
331 fn main() {
332     let a: HasDefault<bool> = make();
333 }
334 "#,
335         );
336     }
337
338     #[test]
339     fn replaces_turbofish_try_await() {
340         check_assist(
341             replace_turbofish_with_explicit_type,
342             r#"
343 //- minicore: option, future
344 struct Fut<T>(T);
345 impl<T> core::future::Future for Fut<T> {
346     type Output = Option<T>;
347 }
348 fn make<T>() -> Fut<T> {}
349 fn main() {
350     let a = make$0::<bool>().await?;
351 }
352 "#,
353             r#"
354 struct Fut<T>(T);
355 impl<T> core::future::Future for Fut<T> {
356     type Output = Option<T>;
357 }
358 fn make<T>() -> Fut<T> {}
359 fn main() {
360     let a: bool = make().await?;
361 }
362 "#,
363         );
364     }
365 }