]> git.lizzy.rs Git - rust.git/blob - src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_type.rs
Rollup merge of #101420 - kraktus:doc_hir_local, r=cjgillot
[rust.git] / src / tools / rust-analyzer / crates / ide-assists / src / handlers / add_explicit_type.rs
1 use hir::HirDisplay;
2 use ide_db::syntax_helpers::node_ext::walk_ty;
3 use syntax::ast::{self, AstNode, LetStmt, Param};
4
5 use crate::{AssistContext, AssistId, AssistKind, Assists};
6
7 // Assist: add_explicit_type
8 //
9 // Specify type for a let binding.
10 //
11 // ```
12 // fn main() {
13 //     let x$0 = 92;
14 // }
15 // ```
16 // ->
17 // ```
18 // fn main() {
19 //     let x: i32 = 92;
20 // }
21 // ```
22 pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
23     let (ascribed_ty, expr, pat) = if let Some(let_stmt) = ctx.find_node_at_offset::<LetStmt>() {
24         let cursor_in_range = {
25             let eq_range = let_stmt.eq_token()?.text_range();
26             ctx.offset() < eq_range.start()
27         };
28         if !cursor_in_range {
29             cov_mark::hit!(add_explicit_type_not_applicable_if_cursor_after_equals);
30             return None;
31         }
32
33         (let_stmt.ty(), let_stmt.initializer(), let_stmt.pat()?)
34     } else if let Some(param) = ctx.find_node_at_offset::<Param>() {
35         if param.syntax().ancestors().nth(2).and_then(ast::ClosureExpr::cast).is_none() {
36             cov_mark::hit!(add_explicit_type_not_applicable_in_fn_param);
37             return None;
38         }
39         (param.ty(), None, param.pat()?)
40     } else {
41         return None;
42     };
43
44     let module = ctx.sema.scope(pat.syntax())?.module();
45     let pat_range = pat.syntax().text_range();
46
47     // Don't enable the assist if there is a type ascription without any placeholders
48     if let Some(ty) = &ascribed_ty {
49         let mut contains_infer_ty = false;
50         walk_ty(ty, &mut |ty| contains_infer_ty |= matches!(ty, ast::Type::InferType(_)));
51         if !contains_infer_ty {
52             cov_mark::hit!(add_explicit_type_not_applicable_if_ty_already_specified);
53             return None;
54         }
55     }
56
57     let ty = match (pat, expr) {
58         (ast::Pat::IdentPat(_), Some(expr)) => ctx.sema.type_of_expr(&expr)?,
59         (pat, _) => ctx.sema.type_of_pat(&pat)?,
60     }
61     .adjusted();
62
63     // Fully unresolved or unnameable types can't be annotated
64     if (ty.contains_unknown() && ty.type_arguments().count() == 0) || ty.is_closure() {
65         cov_mark::hit!(add_explicit_type_not_applicable_if_ty_not_inferred);
66         return None;
67     }
68
69     let inferred_type = ty.display_source_code(ctx.db(), module.into()).ok()?;
70     acc.add(
71         AssistId("add_explicit_type", AssistKind::RefactorRewrite),
72         format!("Insert explicit type `{}`", inferred_type),
73         pat_range,
74         |builder| match ascribed_ty {
75             Some(ascribed_ty) => {
76                 builder.replace(ascribed_ty.syntax().text_range(), inferred_type);
77             }
78             None => {
79                 builder.insert(pat_range.end(), format!(": {}", inferred_type));
80             }
81         },
82     )
83 }
84
85 #[cfg(test)]
86 mod tests {
87     use super::*;
88
89     use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
90
91     #[test]
92     fn add_explicit_type_target() {
93         check_assist_target(add_explicit_type, r#"fn f() { let a$0 = 1; }"#, "a");
94     }
95
96     #[test]
97     fn add_explicit_type_simple() {
98         check_assist(
99             add_explicit_type,
100             r#"fn f() { let a$0 = 1; }"#,
101             r#"fn f() { let a: i32 = 1; }"#,
102         );
103     }
104
105     #[test]
106     fn add_explicit_type_simple_on_infer_ty() {
107         check_assist(
108             add_explicit_type,
109             r#"fn f() { let a$0: _ = 1; }"#,
110             r#"fn f() { let a: i32 = 1; }"#,
111         );
112     }
113
114     #[test]
115     fn add_explicit_type_simple_nested_infer_ty() {
116         check_assist(
117             add_explicit_type,
118             r#"
119 //- minicore: option
120 fn f() {
121     let a$0: Option<_> = Option::Some(1);
122 }
123 "#,
124             r#"
125 fn f() {
126     let a: Option<i32> = Option::Some(1);
127 }
128 "#,
129         );
130     }
131
132     #[test]
133     fn add_explicit_type_macro_call_expr() {
134         check_assist(
135             add_explicit_type,
136             r"macro_rules! v { () => {0u64} } fn f() { let a$0 = v!(); }",
137             r"macro_rules! v { () => {0u64} } fn f() { let a: u64 = v!(); }",
138         );
139     }
140
141     #[test]
142     fn add_explicit_type_not_applicable_for_fully_unresolved() {
143         cov_mark::check!(add_explicit_type_not_applicable_if_ty_not_inferred);
144         check_assist_not_applicable(add_explicit_type, r#"fn f() { let a$0 = None; }"#);
145     }
146
147     #[test]
148     fn add_explicit_type_applicable_for_partially_unresolved() {
149         check_assist(
150             add_explicit_type,
151             r#"
152         struct Vec<T, V> { t: T, v: V }
153         impl<T> Vec<T, Vec<ZZZ, i32>> {
154             fn new() -> Self {
155                 panic!()
156             }
157         }
158         fn f() { let a$0 = Vec::new(); }"#,
159             r#"
160         struct Vec<T, V> { t: T, v: V }
161         impl<T> Vec<T, Vec<ZZZ, i32>> {
162             fn new() -> Self {
163                 panic!()
164             }
165         }
166         fn f() { let a: Vec<_, Vec<_, i32>> = Vec::new(); }"#,
167         );
168     }
169
170     #[test]
171     fn add_explicit_type_not_applicable_closure_expr() {
172         check_assist_not_applicable(add_explicit_type, r#"fn f() { let a$0 = || {}; }"#);
173     }
174
175     #[test]
176     fn add_explicit_type_not_applicable_ty_already_specified() {
177         cov_mark::check!(add_explicit_type_not_applicable_if_ty_already_specified);
178         check_assist_not_applicable(add_explicit_type, r#"fn f() { let a$0: i32 = 1; }"#);
179     }
180
181     #[test]
182     fn add_explicit_type_not_applicable_cursor_after_equals_of_let() {
183         cov_mark::check!(add_explicit_type_not_applicable_if_cursor_after_equals);
184         check_assist_not_applicable(
185             add_explicit_type,
186             r#"fn f() {let a =$0 match 1 {2 => 3, 3 => 5};}"#,
187         )
188     }
189
190     /// https://github.com/rust-lang/rust-analyzer/issues/2922
191     #[test]
192     fn regression_issue_2922() {
193         check_assist(
194             add_explicit_type,
195             r#"
196 fn main() {
197     let $0v = [0.0; 2];
198 }
199 "#,
200             r#"
201 fn main() {
202     let v: [f64; 2] = [0.0; 2];
203 }
204 "#,
205         );
206         // note: this may break later if we add more consteval. it just needs to be something that our
207         // consteval engine doesn't understand
208         check_assist_not_applicable(
209             add_explicit_type,
210             r#"
211 //- minicore: option
212
213 fn main() {
214     let $0l = [0.0; Some(2).unwrap()];
215 }
216 "#,
217         );
218     }
219
220     #[test]
221     fn default_generics_should_not_be_added() {
222         check_assist(
223             add_explicit_type,
224             r#"
225 struct Test<K, T = u8> { k: K, t: T }
226
227 fn main() {
228     let test$0 = Test { t: 23u8, k: 33 };
229 }
230 "#,
231             r#"
232 struct Test<K, T = u8> { k: K, t: T }
233
234 fn main() {
235     let test: Test<i32> = Test { t: 23u8, k: 33 };
236 }
237 "#,
238         );
239     }
240
241     #[test]
242     fn type_should_be_added_after_pattern() {
243         // LetStmt = Attr* 'let' Pat (':' Type)? '=' initializer:Expr ';'
244         check_assist(
245             add_explicit_type,
246             r#"
247 fn main() {
248     let $0test @ () = ();
249 }
250 "#,
251             r#"
252 fn main() {
253     let test @ (): () = ();
254 }
255 "#,
256         );
257     }
258
259     #[test]
260     fn add_explicit_type_inserts_coercions() {
261         check_assist(
262             add_explicit_type,
263             r#"
264 //- minicore: coerce_unsized
265 fn f() {
266     let $0x: *const [_] = &[3];
267 }
268 "#,
269             r#"
270 fn f() {
271     let x: *const [i32] = &[3];
272 }
273 "#,
274         );
275     }
276
277     #[test]
278     fn add_explicit_type_not_applicable_fn_param() {
279         cov_mark::check!(add_explicit_type_not_applicable_in_fn_param);
280         check_assist_not_applicable(add_explicit_type, r#"fn f(x$0: ()) {}"#);
281     }
282
283     #[test]
284     fn add_explicit_type_ascribes_closure_param() {
285         check_assist(
286             add_explicit_type,
287             r#"
288 fn f() {
289     |y$0| {
290         let x: i32 = y;
291     };
292 }
293 "#,
294             r#"
295 fn f() {
296     |y: i32| {
297         let x: i32 = y;
298     };
299 }
300 "#,
301         );
302     }
303
304     #[test]
305     fn add_explicit_type_ascribes_closure_param_already_ascribed() {
306         check_assist(
307             add_explicit_type,
308             r#"
309 //- minicore: option
310 fn f() {
311     |mut y$0: Option<_>| {
312         y = Some(3);
313     };
314 }
315 "#,
316             r#"
317 fn f() {
318     |mut y: Option<i32>| {
319         y = Some(3);
320     };
321 }
322 "#,
323         );
324     }
325 }