]> git.lizzy.rs Git - rust.git/blob - crates/ide_assists/src/handlers/add_turbo_fish.rs
Use upstream cov-mark
[rust.git] / crates / ide_assists / src / handlers / add_turbo_fish.rs
1 use ide_db::defs::{Definition, NameRefClass};
2 use syntax::{ast, AstNode, SyntaxKind, T};
3
4 use crate::{
5     assist_context::{AssistContext, Assists},
6     AssistId, AssistKind,
7 };
8
9 // Assist: add_turbo_fish
10 //
11 // Adds `::<_>` to a call of a generic method or function.
12 //
13 // ```
14 // fn make<T>() -> T { todo!() }
15 // fn main() {
16 //     let x = make$0();
17 // }
18 // ```
19 // ->
20 // ```
21 // fn make<T>() -> T { todo!() }
22 // fn main() {
23 //     let x = make::<${0:_}>();
24 // }
25 // ```
26 pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
27     let ident = ctx.find_token_syntax_at_offset(SyntaxKind::IDENT).or_else(|| {
28         let arg_list = ctx.find_node_at_offset::<ast::ArgList>()?;
29         if arg_list.args().count() > 0 {
30             return None;
31         }
32         cov_mark::hit!(add_turbo_fish_after_call);
33         cov_mark::hit!(add_type_ascription_after_call);
34         arg_list.l_paren_token()?.prev_token().filter(|it| it.kind() == SyntaxKind::IDENT)
35     })?;
36     let next_token = ident.next_token()?;
37     if next_token.kind() == T![::] {
38         cov_mark::hit!(add_turbo_fish_one_fish_is_enough);
39         return None;
40     }
41     let name_ref = ast::NameRef::cast(ident.parent())?;
42     let def = match NameRefClass::classify(&ctx.sema, &name_ref)? {
43         NameRefClass::Definition(def) => def,
44         NameRefClass::ExternCrate(_) | NameRefClass::FieldShorthand { .. } => return None,
45     };
46     let fun = match def {
47         Definition::ModuleDef(hir::ModuleDef::Function(it)) => it,
48         _ => return None,
49     };
50     let generics = hir::GenericDef::Function(fun).params(ctx.sema.db);
51     if generics.is_empty() {
52         cov_mark::hit!(add_turbo_fish_non_generic);
53         return None;
54     }
55
56     if let Some(let_stmt) = ctx.find_node_at_offset::<ast::LetStmt>() {
57         if let_stmt.colon_token().is_none() {
58             let type_pos = let_stmt.pat()?.syntax().last_token()?.text_range().end();
59             acc.add(
60                 AssistId("add_type_ascription", AssistKind::RefactorRewrite),
61                 "Add `: _` before assignment operator",
62                 ident.text_range(),
63                 |builder| match ctx.config.snippet_cap {
64                     Some(cap) => builder.insert_snippet(cap, type_pos, ": ${0:_}"),
65                     None => builder.insert(type_pos, ": _"),
66                 },
67             )?
68         } else {
69             cov_mark::hit!(add_type_ascription_already_typed);
70         }
71     }
72
73     acc.add(
74         AssistId("add_turbo_fish", AssistKind::RefactorRewrite),
75         "Add `::<>`",
76         ident.text_range(),
77         |builder| match ctx.config.snippet_cap {
78             Some(cap) => builder.insert_snippet(cap, ident.text_range().end(), "::<${0:_}>"),
79             None => builder.insert(ident.text_range().end(), "::<_>"),
80         },
81     )
82 }
83
84 #[cfg(test)]
85 mod tests {
86     use crate::tests::{check_assist, check_assist_by_label, check_assist_not_applicable};
87
88     use super::*;
89
90     #[test]
91     fn add_turbo_fish_function() {
92         check_assist(
93             add_turbo_fish,
94             r#"
95 fn make<T>() -> T {}
96 fn main() {
97     make$0();
98 }
99 "#,
100             r#"
101 fn make<T>() -> T {}
102 fn main() {
103     make::<${0:_}>();
104 }
105 "#,
106         );
107     }
108
109     #[test]
110     fn add_turbo_fish_after_call() {
111         cov_mark::check!(add_turbo_fish_after_call);
112         check_assist(
113             add_turbo_fish,
114             r#"
115 fn make<T>() -> T {}
116 fn main() {
117     make()$0;
118 }
119 "#,
120             r#"
121 fn make<T>() -> T {}
122 fn main() {
123     make::<${0:_}>();
124 }
125 "#,
126         );
127     }
128
129     #[test]
130     fn add_turbo_fish_method() {
131         check_assist(
132             add_turbo_fish,
133             r#"
134 struct S;
135 impl S {
136     fn make<T>(&self) -> T {}
137 }
138 fn main() {
139     S.make$0();
140 }
141 "#,
142             r#"
143 struct S;
144 impl S {
145     fn make<T>(&self) -> T {}
146 }
147 fn main() {
148     S.make::<${0:_}>();
149 }
150 "#,
151         );
152     }
153
154     #[test]
155     fn add_turbo_fish_one_fish_is_enough() {
156         cov_mark::check!(add_turbo_fish_one_fish_is_enough);
157         check_assist_not_applicable(
158             add_turbo_fish,
159             r#"
160 fn make<T>() -> T {}
161 fn main() {
162     make$0::<()>();
163 }
164 "#,
165         );
166     }
167
168     #[test]
169     fn add_turbo_fish_non_generic() {
170         cov_mark::check!(add_turbo_fish_non_generic);
171         check_assist_not_applicable(
172             add_turbo_fish,
173             r#"
174 fn make() -> () {}
175 fn main() {
176     make$0();
177 }
178 "#,
179         );
180     }
181
182     #[test]
183     fn add_type_ascription_function() {
184         check_assist_by_label(
185             add_turbo_fish,
186             r#"
187 fn make<T>() -> T {}
188 fn main() {
189     let x = make$0();
190 }
191 "#,
192             r#"
193 fn make<T>() -> T {}
194 fn main() {
195     let x: ${0:_} = make();
196 }
197 "#,
198             "Add `: _` before assignment operator",
199         );
200     }
201
202     #[test]
203     fn add_type_ascription_after_call() {
204         cov_mark::check!(add_type_ascription_after_call);
205         check_assist_by_label(
206             add_turbo_fish,
207             r#"
208 fn make<T>() -> T {}
209 fn main() {
210     let x = make()$0;
211 }
212 "#,
213             r#"
214 fn make<T>() -> T {}
215 fn main() {
216     let x: ${0:_} = make();
217 }
218 "#,
219             "Add `: _` before assignment operator",
220         );
221     }
222
223     #[test]
224     fn add_type_ascription_method() {
225         check_assist_by_label(
226             add_turbo_fish,
227             r#"
228 struct S;
229 impl S {
230     fn make<T>(&self) -> T {}
231 }
232 fn main() {
233     let x = S.make$0();
234 }
235 "#,
236             r#"
237 struct S;
238 impl S {
239     fn make<T>(&self) -> T {}
240 }
241 fn main() {
242     let x: ${0:_} = S.make();
243 }
244 "#,
245             "Add `: _` before assignment operator",
246         );
247     }
248
249     #[test]
250     fn add_type_ascription_already_typed() {
251         cov_mark::check!(add_type_ascription_already_typed);
252         check_assist(
253             add_turbo_fish,
254             r#"
255 fn make<T>() -> T {}
256 fn main() {
257     let x: () = make$0();
258 }
259 "#,
260             r#"
261 fn make<T>() -> T {}
262 fn main() {
263     let x: () = make::<${0:_}>();
264 }
265 "#,
266         );
267     }
268 }