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