]> git.lizzy.rs Git - rust.git/blob - crates/ra_assists/src/handlers/add_explicit_type.rs
a59ec16b2d9ff4b6a2da199c1e0426eed98db93c
[rust.git] / crates / ra_assists / src / handlers / add_explicit_type.rs
1 use hir::HirDisplay;
2 use ra_syntax::{
3     ast::{self, AstNode, LetStmt, NameOwner, TypeAscriptionOwner},
4     TextRange,
5 };
6
7 use crate::{Assist, AssistCtx, AssistId};
8
9 // Assist: add_explicit_type
10 //
11 // Specify type for a let binding.
12 //
13 // ```
14 // fn main() {
15 //     let x<|> = 92;
16 // }
17 // ```
18 // ->
19 // ```
20 // fn main() {
21 //     let x: i32 = 92;
22 // }
23 // ```
24 pub(crate) fn add_explicit_type(ctx: AssistCtx) -> Option<Assist> {
25     let stmt = ctx.find_node_at_offset::<LetStmt>()?;
26     let expr = stmt.initializer()?;
27     let pat = stmt.pat()?;
28     // Must be a binding
29     let pat = match pat {
30         ast::Pat::BindPat(bind_pat) => bind_pat,
31         _ => return None,
32     };
33     let pat_range = pat.syntax().text_range();
34     // The binding must have a name
35     let name = pat.name()?;
36     let name_range = name.syntax().text_range();
37     let stmt_range = stmt.syntax().text_range();
38     let eq_range = stmt.eq_token()?.text_range();
39     // Assist should only be applicable if cursor is between 'let' and '='
40     let let_range = TextRange::new(stmt_range.start(), eq_range.start());
41     let cursor_in_range = let_range.contains_range(ctx.frange.range);
42     if !cursor_in_range {
43         return None;
44     }
45     // Assist not applicable if the type has already been specified
46     // and it has no placeholders
47     let ascribed_ty = stmt.ascribed_type();
48     if let Some(ref ty) = ascribed_ty {
49         if ty.syntax().descendants().find_map(ast::PlaceholderType::cast).is_none() {
50             return None;
51         }
52     }
53     // Infer type
54     let ty = ctx.sema.type_of_expr(&expr)?;
55
56     if ty.contains_unknown() || ty.is_closure() {
57         return None;
58     }
59
60     let db = ctx.db;
61     let new_type_string = ty.display_truncated(db, None).to_string();
62     ctx.add_assist(
63         AssistId("add_explicit_type"),
64         format!("Insert explicit type '{}'", new_type_string),
65         pat_range,
66         |edit| {
67             if let Some(ascribed_ty) = ascribed_ty {
68                 edit.replace(ascribed_ty.syntax().text_range(), new_type_string);
69             } else {
70                 edit.insert(name_range.end(), format!(": {}", new_type_string));
71             }
72         },
73     )
74 }
75
76 #[cfg(test)]
77 mod tests {
78     use super::*;
79
80     use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
81
82     #[test]
83     fn add_explicit_type_target() {
84         check_assist_target(add_explicit_type, "fn f() { let a<|> = 1; }", "a");
85     }
86
87     #[test]
88     fn add_explicit_type_works_for_simple_expr() {
89         check_assist(
90             add_explicit_type,
91             "fn f() { let a<|> = 1; }",
92             "fn f() { let a<|>: i32 = 1; }",
93         );
94     }
95
96     #[test]
97     fn add_explicit_type_works_for_underscore() {
98         check_assist(
99             add_explicit_type,
100             "fn f() { let a<|>: _ = 1; }",
101             "fn f() { let a<|>: i32 = 1; }",
102         );
103     }
104
105     #[test]
106     fn add_explicit_type_works_for_nested_underscore() {
107         check_assist(
108             add_explicit_type,
109             r#"
110             enum Option<T> {
111                 Some(T),
112                 None
113             }
114
115             fn f() {
116                 let a<|>: Option<_> = Option::Some(1);
117             }"#,
118             r#"
119             enum Option<T> {
120                 Some(T),
121                 None
122             }
123
124             fn f() {
125                 let a<|>: Option<i32> = Option::Some(1);
126             }"#,
127         );
128     }
129
130     #[test]
131     fn add_explicit_type_works_for_macro_call() {
132         check_assist(
133             add_explicit_type,
134             r"macro_rules! v { () => {0u64} } fn f() { let a<|> = v!(); }",
135             r"macro_rules! v { () => {0u64} } fn f() { let a<|>: u64 = v!(); }",
136         );
137     }
138
139     #[test]
140     fn add_explicit_type_works_for_macro_call_recursive() {
141         check_assist(
142             add_explicit_type,
143             "macro_rules! u { () => {0u64} } macro_rules! v { () => {u!()} } fn f() { let a<|> = v!(); }",
144             "macro_rules! u { () => {0u64} } macro_rules! v { () => {u!()} } fn f() { let a<|>: u64 = v!(); }",
145         );
146     }
147
148     #[test]
149     fn add_explicit_type_not_applicable_if_ty_not_inferred() {
150         check_assist_not_applicable(add_explicit_type, "fn f() { let a<|> = None; }");
151     }
152
153     #[test]
154     fn add_explicit_type_not_applicable_if_ty_already_specified() {
155         check_assist_not_applicable(add_explicit_type, "fn f() { let a<|>: i32 = 1; }");
156     }
157
158     #[test]
159     fn add_explicit_type_not_applicable_if_specified_ty_is_tuple() {
160         check_assist_not_applicable(add_explicit_type, "fn f() { let a<|>: (i32, i32) = (3, 4); }");
161     }
162
163     #[test]
164     fn add_explicit_type_not_applicable_if_cursor_after_equals() {
165         check_assist_not_applicable(
166             add_explicit_type,
167             "fn f() {let a =<|> match 1 {2 => 3, 3 => 5};}",
168         )
169     }
170
171     #[test]
172     fn add_explicit_type_not_applicable_if_cursor_before_let() {
173         check_assist_not_applicable(
174             add_explicit_type,
175             "fn f() <|>{let a = match 1 {2 => 3, 3 => 5};}",
176         )
177     }
178
179     #[test]
180     fn closure_parameters_are_not_added() {
181         check_assist_not_applicable(
182             add_explicit_type,
183             r#"
184 fn main() {
185     let multiply_by_two<|> = |i| i * 3;
186     let six = multiply_by_two(2);
187 }"#,
188         )
189     }
190
191     #[test]
192     fn default_generics_should_not_be_added() {
193         check_assist(
194             add_explicit_type,
195             r#"
196 struct Test<K, T = u8> {
197     k: K,
198     t: T,
199 }
200
201 fn main() {
202     let test<|> = Test { t: 23, k: 33 };
203 }"#,
204             r#"
205 struct Test<K, T = u8> {
206     k: K,
207     t: T,
208 }
209
210 fn main() {
211     let test<|>: Test<i32> = Test { t: 23, k: 33 };
212 }"#,
213         );
214     }
215 }