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