]> git.lizzy.rs Git - rust.git/blob - crates/ide_completion/src/render/function.rs
Merge #8142
[rust.git] / crates / ide_completion / src / render / function.rs
1 //! Renderer for function calls.
2
3 use hir::{HasSource, HirDisplay, Type};
4 use ide_db::SymbolKind;
5 use itertools::Itertools;
6 use syntax::ast::Fn;
7
8 use crate::{
9     item::{CompletionItem, CompletionItemKind, CompletionKind, CompletionRelevance, ImportEdit},
10     render::{
11         builder_ext::Params, compute_exact_name_match, compute_exact_type_match, compute_ref_match,
12         RenderContext,
13     },
14 };
15
16 pub(crate) fn render_fn<'a>(
17     ctx: RenderContext<'a>,
18     import_to_add: Option<ImportEdit>,
19     local_name: Option<String>,
20     fn_: hir::Function,
21 ) -> Option<CompletionItem> {
22     let _p = profile::span("render_fn");
23     Some(FunctionRender::new(ctx, local_name, fn_, false)?.render(import_to_add))
24 }
25
26 pub(crate) fn render_method<'a>(
27     ctx: RenderContext<'a>,
28     import_to_add: Option<ImportEdit>,
29     local_name: Option<String>,
30     fn_: hir::Function,
31 ) -> Option<CompletionItem> {
32     let _p = profile::span("render_method");
33     Some(FunctionRender::new(ctx, local_name, fn_, true)?.render(import_to_add))
34 }
35
36 #[derive(Debug)]
37 struct FunctionRender<'a> {
38     ctx: RenderContext<'a>,
39     name: String,
40     func: hir::Function,
41     ast_node: Fn,
42     is_method: bool,
43 }
44
45 impl<'a> FunctionRender<'a> {
46     fn new(
47         ctx: RenderContext<'a>,
48         local_name: Option<String>,
49         fn_: hir::Function,
50         is_method: bool,
51     ) -> Option<FunctionRender<'a>> {
52         let name = local_name.unwrap_or_else(|| fn_.name(ctx.db()).to_string());
53         let ast_node = fn_.source(ctx.db())?.value;
54
55         Some(FunctionRender { ctx, name, func: fn_, ast_node, is_method })
56     }
57
58     fn render(self, import_to_add: Option<ImportEdit>) -> CompletionItem {
59         let params = self.params();
60         let mut item = CompletionItem::new(
61             CompletionKind::Reference,
62             self.ctx.source_range(),
63             self.name.clone(),
64         );
65         item.kind(self.kind())
66             .set_documentation(self.ctx.docs(self.func))
67             .set_deprecated(
68                 self.ctx.is_deprecated(self.func) || self.ctx.is_deprecated_assoc_item(self.func),
69             )
70             .detail(self.detail())
71             .add_call_parens(self.ctx.completion, self.name.clone(), params)
72             .add_import(import_to_add);
73
74         let ret_type = self.func.ret_type(self.ctx.db());
75         item.set_relevance(CompletionRelevance {
76             exact_type_match: compute_exact_type_match(self.ctx.completion, &ret_type),
77             exact_name_match: compute_exact_name_match(self.ctx.completion, self.name.clone()),
78             ..CompletionRelevance::default()
79         });
80
81         if let Some(ref_match) = compute_ref_match(self.ctx.completion, &ret_type) {
82             // FIXME
83             // For now we don't properly calculate the edits for ref match
84             // completions on methods, so we've disabled them. See #8058.
85             if !self.is_method {
86                 item.ref_match(ref_match);
87             }
88         }
89
90         item.build()
91     }
92
93     fn detail(&self) -> String {
94         let ret_ty = self.func.ret_type(self.ctx.db());
95         let ret = if ret_ty.is_unit() {
96             // Omit the return type if it is the unit type
97             String::new()
98         } else {
99             format!(" {}", self.ty_display())
100         };
101
102         format!("fn({}){}", self.params_display(), ret)
103     }
104
105     fn params_display(&self) -> String {
106         if let Some(self_param) = self.func.self_param(self.ctx.db()) {
107             let params = self
108                 .func
109                 .assoc_fn_params(self.ctx.db())
110                 .into_iter()
111                 .skip(1) // skip the self param because we are manually handling that
112                 .map(|p| p.ty().display(self.ctx.db()).to_string());
113
114             std::iter::once(self_param.display(self.ctx.db()).to_owned()).chain(params).join(", ")
115         } else {
116             let params = self
117                 .func
118                 .assoc_fn_params(self.ctx.db())
119                 .into_iter()
120                 .map(|p| p.ty().display(self.ctx.db()).to_string())
121                 .join(", ");
122             params
123         }
124     }
125
126     fn ty_display(&self) -> String {
127         let ret_ty = self.func.ret_type(self.ctx.db());
128
129         format!("-> {}", ret_ty.display(self.ctx.db()))
130     }
131
132     fn add_arg(&self, arg: &str, ty: &Type) -> String {
133         if let Some(derefed_ty) = ty.remove_ref() {
134             for (name, local) in self.ctx.completion.locals.iter() {
135                 if name == arg && local.ty(self.ctx.db()) == derefed_ty {
136                     let mutability = if ty.is_mutable_reference() { "&mut " } else { "&" };
137                     return format!("{}{}", mutability, arg);
138                 }
139             }
140         }
141         arg.to_string()
142     }
143
144     fn params(&self) -> Params {
145         let ast_params = match self.ast_node.param_list() {
146             Some(it) => it,
147             None => return Params::Named(Vec::new()),
148         };
149
150         let mut params_pats = Vec::new();
151         let params_ty = if self.ctx.completion.dot_receiver.is_some() {
152             self.func.method_params(self.ctx.db()).unwrap_or_default()
153         } else {
154             if let Some(s) = ast_params.self_param() {
155                 cov_mark::hit!(parens_for_method_call_as_assoc_fn);
156                 params_pats.push(Some(s.to_string()));
157             }
158             self.func.assoc_fn_params(self.ctx.db())
159         };
160         params_pats
161             .extend(ast_params.params().into_iter().map(|it| it.pat().map(|it| it.to_string())));
162
163         let params = params_pats
164             .into_iter()
165             .zip(params_ty)
166             .flat_map(|(pat, param_ty)| {
167                 let pat = pat?;
168                 let name = pat;
169                 let arg = name.trim_start_matches("mut ").trim_start_matches('_');
170                 Some(self.add_arg(arg, param_ty.ty()))
171             })
172             .collect();
173         Params::Named(params)
174     }
175
176     fn kind(&self) -> CompletionItemKind {
177         if self.func.self_param(self.ctx.db()).is_some() {
178             CompletionItemKind::Method
179         } else {
180             SymbolKind::Function.into()
181         }
182     }
183 }
184
185 #[cfg(test)]
186 mod tests {
187     use crate::{
188         test_utils::{check_edit, check_edit_with_config, TEST_CONFIG},
189         CompletionConfig,
190     };
191
192     #[test]
193     fn inserts_parens_for_function_calls() {
194         cov_mark::check!(inserts_parens_for_function_calls);
195         check_edit(
196             "no_args",
197             r#"
198 fn no_args() {}
199 fn main() { no_$0 }
200 "#,
201             r#"
202 fn no_args() {}
203 fn main() { no_args()$0 }
204 "#,
205         );
206
207         check_edit(
208             "with_args",
209             r#"
210 fn with_args(x: i32, y: String) {}
211 fn main() { with_$0 }
212 "#,
213             r#"
214 fn with_args(x: i32, y: String) {}
215 fn main() { with_args(${1:x}, ${2:y})$0 }
216 "#,
217         );
218
219         check_edit(
220             "foo",
221             r#"
222 struct S;
223 impl S {
224     fn foo(&self) {}
225 }
226 fn bar(s: &S) { s.f$0 }
227 "#,
228             r#"
229 struct S;
230 impl S {
231     fn foo(&self) {}
232 }
233 fn bar(s: &S) { s.foo()$0 }
234 "#,
235         );
236
237         check_edit(
238             "foo",
239             r#"
240 struct S {}
241 impl S {
242     fn foo(&self, x: i32) {}
243 }
244 fn bar(s: &S) {
245     s.f$0
246 }
247 "#,
248             r#"
249 struct S {}
250 impl S {
251     fn foo(&self, x: i32) {}
252 }
253 fn bar(s: &S) {
254     s.foo(${1:x})$0
255 }
256 "#,
257         );
258     }
259
260     #[test]
261     fn parens_for_method_call_as_assoc_fn() {
262         cov_mark::check!(parens_for_method_call_as_assoc_fn);
263         check_edit(
264             "foo",
265             r#"
266 struct S;
267 impl S {
268     fn foo(&self) {}
269 }
270 fn main() { S::f$0 }
271 "#,
272             r#"
273 struct S;
274 impl S {
275     fn foo(&self) {}
276 }
277 fn main() { S::foo(${1:&self})$0 }
278 "#,
279         );
280     }
281
282     #[test]
283     fn suppress_arg_snippets() {
284         cov_mark::check!(suppress_arg_snippets);
285         check_edit_with_config(
286             CompletionConfig { add_call_argument_snippets: false, ..TEST_CONFIG },
287             "with_args",
288             r#"
289 fn with_args(x: i32, y: String) {}
290 fn main() { with_$0 }
291 "#,
292             r#"
293 fn with_args(x: i32, y: String) {}
294 fn main() { with_args($0) }
295 "#,
296         );
297     }
298
299     #[test]
300     fn strips_underscores_from_args() {
301         check_edit(
302             "foo",
303             r#"
304 fn foo(_foo: i32, ___bar: bool, ho_ge_: String) {}
305 fn main() { f$0 }
306 "#,
307             r#"
308 fn foo(_foo: i32, ___bar: bool, ho_ge_: String) {}
309 fn main() { foo(${1:foo}, ${2:bar}, ${3:ho_ge_})$0 }
310 "#,
311         );
312     }
313
314     #[test]
315     fn insert_ref_when_matching_local_in_scope() {
316         check_edit(
317             "ref_arg",
318             r#"
319 struct Foo {}
320 fn ref_arg(x: &Foo) {}
321 fn main() {
322     let x = Foo {};
323     ref_ar$0
324 }
325 "#,
326             r#"
327 struct Foo {}
328 fn ref_arg(x: &Foo) {}
329 fn main() {
330     let x = Foo {};
331     ref_arg(${1:&x})$0
332 }
333 "#,
334         );
335     }
336
337     #[test]
338     fn insert_mut_ref_when_matching_local_in_scope() {
339         check_edit(
340             "ref_arg",
341             r#"
342 struct Foo {}
343 fn ref_arg(x: &mut Foo) {}
344 fn main() {
345     let x = Foo {};
346     ref_ar$0
347 }
348 "#,
349             r#"
350 struct Foo {}
351 fn ref_arg(x: &mut Foo) {}
352 fn main() {
353     let x = Foo {};
354     ref_arg(${1:&mut x})$0
355 }
356 "#,
357         );
358     }
359
360     #[test]
361     fn insert_ref_when_matching_local_in_scope_for_method() {
362         check_edit(
363             "apply_foo",
364             r#"
365 struct Foo {}
366 struct Bar {}
367 impl Bar {
368     fn apply_foo(&self, x: &Foo) {}
369 }
370
371 fn main() {
372     let x = Foo {};
373     let y = Bar {};
374     y.$0
375 }
376 "#,
377             r#"
378 struct Foo {}
379 struct Bar {}
380 impl Bar {
381     fn apply_foo(&self, x: &Foo) {}
382 }
383
384 fn main() {
385     let x = Foo {};
386     let y = Bar {};
387     y.apply_foo(${1:&x})$0
388 }
389 "#,
390         );
391     }
392
393     #[test]
394     fn trim_mut_keyword_in_func_completion() {
395         check_edit(
396             "take_mutably",
397             r#"
398 fn take_mutably(mut x: &i32) {}
399
400 fn main() {
401     take_m$0
402 }
403 "#,
404             r#"
405 fn take_mutably(mut x: &i32) {}
406
407 fn main() {
408     take_mutably(${1:x})$0
409 }
410 "#,
411         );
412     }
413 }