1 //! Renderer for function calls.
2 
3 use either::Either;
4 use hir::{AsAssocItem, HasSource, HirDisplay};
5 use ide_db::SymbolKind;
6 use itertools::Itertools;
7 use syntax::ast;
8 
9 use crate::{
10     item::{CompletionItem, CompletionItemKind, CompletionRelevance, ImportEdit},
11     render::{
12         builder_ext::Params, compute_exact_name_match, compute_ref_match, compute_type_match,
13         RenderContext,
14     },
15 };
16 
render_fn( ctx: RenderContext<'_>, import_to_add: Option<ImportEdit>, local_name: Option<hir::Name>, fn_: hir::Function, ) -> Option<CompletionItem>17 pub(crate) fn render_fn(
18     ctx: RenderContext<'_>,
19     import_to_add: Option<ImportEdit>,
20     local_name: Option<hir::Name>,
21     fn_: hir::Function,
22 ) -> Option<CompletionItem> {
23     let _p = profile::span("render_fn");
24     Some(FunctionRender::new(ctx, None, local_name, fn_, false)?.render(import_to_add))
25 }
26 
render_method( ctx: RenderContext<'_>, import_to_add: Option<ImportEdit>, receiver: Option<hir::Name>, local_name: Option<hir::Name>, fn_: hir::Function, ) -> Option<CompletionItem>27 pub(crate) fn render_method(
28     ctx: RenderContext<'_>,
29     import_to_add: Option<ImportEdit>,
30     receiver: Option<hir::Name>,
31     local_name: Option<hir::Name>,
32     fn_: hir::Function,
33 ) -> Option<CompletionItem> {
34     let _p = profile::span("render_method");
35     Some(FunctionRender::new(ctx, receiver, local_name, fn_, true)?.render(import_to_add))
36 }
37 
38 #[derive(Debug)]
39 struct FunctionRender<'a> {
40     ctx: RenderContext<'a>,
41     name: hir::Name,
42     receiver: Option<hir::Name>,
43     func: hir::Function,
44     /// NB: having `ast::Fn` here might or might not be a good idea. The problem
45     /// with it is that, to get an `ast::`, you want to parse the corresponding
46     /// source file. So, when flyimport completions suggest a bunch of
47     /// functions, we spend quite some time parsing many files.
48     ///
49     /// We need ast because we want to access parameter names (patterns). We can
50     /// add them to the hir of the function itself, but parameter names are not
51     /// something hir cares otherwise.
52     ///
53     /// Alternatively we can reconstruct params from the function body, but that
54     /// would require parsing anyway.
55     ///
56     /// It seems that just using `ast` is the best choice -- most of parses
57     /// should be cached anyway.
58     ast_node: ast::Fn,
59     is_method: bool,
60 }
61 
62 impl<'a> FunctionRender<'a> {
new( ctx: RenderContext<'a>, receiver: Option<hir::Name>, local_name: Option<hir::Name>, fn_: hir::Function, is_method: bool, ) -> Option<FunctionRender<'a>>63     fn new(
64         ctx: RenderContext<'a>,
65         receiver: Option<hir::Name>,
66         local_name: Option<hir::Name>,
67         fn_: hir::Function,
68         is_method: bool,
69     ) -> Option<FunctionRender<'a>> {
70         let name = local_name.unwrap_or_else(|| fn_.name(ctx.db()));
71         let ast_node = fn_.source(ctx.db())?.value;
72 
73         Some(FunctionRender { ctx, name, receiver, func: fn_, ast_node, is_method })
74     }
75 
render(self, import_to_add: Option<ImportEdit>) -> CompletionItem76     fn render(self, import_to_add: Option<ImportEdit>) -> CompletionItem {
77         let params = self.params();
78         let call = match &self.receiver {
79             Some(receiver) => format!("{}.{}", receiver, &self.name),
80             None => self.name.to_string(),
81         };
82         let mut item = CompletionItem::new(self.kind(), self.ctx.source_range(), call.clone());
83         item.set_documentation(self.ctx.docs(self.func))
84             .set_deprecated(
85                 self.ctx.is_deprecated(self.func) || self.ctx.is_deprecated_assoc_item(self.func),
86             )
87             .detail(self.detail())
88             .add_call_parens(self.ctx.completion, call.clone(), params);
89 
90         if import_to_add.is_none() {
91             let db = self.ctx.db();
92             if let Some(actm) = self.func.as_assoc_item(db) {
93                 if let Some(trt) = actm.containing_trait_or_trait_impl(db) {
94                     item.trait_name(trt.name(db).to_smol_str());
95                 }
96             }
97         }
98 
99         if let Some(import_to_add) = import_to_add {
100             item.add_import(import_to_add);
101         }
102         item.lookup_by(self.name.to_smol_str());
103 
104         let ret_type = self.func.ret_type(self.ctx.db());
105         item.set_relevance(CompletionRelevance {
106             type_match: compute_type_match(self.ctx.completion, &ret_type),
107             exact_name_match: compute_exact_name_match(self.ctx.completion, &call),
108             ..CompletionRelevance::default()
109         });
110 
111         if let Some(ref_match) = compute_ref_match(self.ctx.completion, &ret_type) {
112             // FIXME
113             // For now we don't properly calculate the edits for ref match
114             // completions on methods, so we've disabled them. See #8058.
115             if !self.is_method {
116                 item.ref_match(ref_match);
117             }
118         }
119 
120         item.build()
121     }
122 
detail(&self) -> String123     fn detail(&self) -> String {
124         let ret_ty = self.func.ret_type(self.ctx.db());
125         let ret = if ret_ty.is_unit() {
126             // Omit the return type if it is the unit type
127             String::new()
128         } else {
129             format!(" {}", self.ty_display())
130         };
131 
132         format!("fn({}){}", self.params_display(), ret)
133     }
134 
params_display(&self) -> String135     fn params_display(&self) -> String {
136         if let Some(self_param) = self.func.self_param(self.ctx.db()) {
137             let params = self
138                 .func
139                 .assoc_fn_params(self.ctx.db())
140                 .into_iter()
141                 .skip(1) // skip the self param because we are manually handling that
142                 .map(|p| p.ty().display(self.ctx.db()).to_string());
143 
144             std::iter::once(self_param.display(self.ctx.db()).to_owned()).chain(params).join(", ")
145         } else {
146             let params = self
147                 .func
148                 .assoc_fn_params(self.ctx.db())
149                 .into_iter()
150                 .map(|p| p.ty().display(self.ctx.db()).to_string())
151                 .join(", ");
152             params
153         }
154     }
155 
ty_display(&self) -> String156     fn ty_display(&self) -> String {
157         let ret_ty = self.func.ret_type(self.ctx.db());
158 
159         format!("-> {}", ret_ty.display(self.ctx.db()))
160     }
161 
params(&self) -> Params162     fn params(&self) -> Params {
163         let ast_params = match self.ast_node.param_list() {
164             Some(it) => it,
165             None => return Params::Named(Vec::new()),
166         };
167         let params = ast_params.params().map(Either::Right);
168 
169         let params = if self.ctx.completion.has_dot_receiver() || self.receiver.is_some() {
170             params.zip(self.func.method_params(self.ctx.db()).unwrap_or_default()).collect()
171         } else {
172             ast_params
173                 .self_param()
174                 .map(Either::Left)
175                 .into_iter()
176                 .chain(params)
177                 .zip(self.func.assoc_fn_params(self.ctx.db()))
178                 .collect()
179         };
180 
181         Params::Named(params)
182     }
183 
kind(&self) -> CompletionItemKind184     fn kind(&self) -> CompletionItemKind {
185         if self.func.self_param(self.ctx.db()).is_some() {
186             CompletionItemKind::Method
187         } else {
188             SymbolKind::Function.into()
189         }
190     }
191 }
192 
193 #[cfg(test)]
194 mod tests {
195     use crate::{
196         tests::{check_edit, check_edit_with_config, TEST_CONFIG},
197         CompletionConfig,
198     };
199 
200     #[test]
inserts_parens_for_function_calls()201     fn inserts_parens_for_function_calls() {
202         cov_mark::check!(inserts_parens_for_function_calls);
203         check_edit(
204             "no_args",
205             r#"
206 fn no_args() {}
207 fn main() { no_$0 }
208 "#,
209             r#"
210 fn no_args() {}
211 fn main() { no_args()$0 }
212 "#,
213         );
214 
215         check_edit(
216             "with_args",
217             r#"
218 fn with_args(x: i32, y: String) {}
219 fn main() { with_$0 }
220 "#,
221             r#"
222 fn with_args(x: i32, y: String) {}
223 fn main() { with_args(${1:x}, ${2:y})$0 }
224 "#,
225         );
226 
227         check_edit(
228             "foo",
229             r#"
230 struct S;
231 impl S {
232     fn foo(&self) {}
233 }
234 fn bar(s: &S) { s.f$0 }
235 "#,
236             r#"
237 struct S;
238 impl S {
239     fn foo(&self) {}
240 }
241 fn bar(s: &S) { s.foo()$0 }
242 "#,
243         );
244 
245         check_edit(
246             "foo",
247             r#"
248 struct S {}
249 impl S {
250     fn foo(&self, x: i32) {}
251 }
252 fn bar(s: &S) {
253     s.f$0
254 }
255 "#,
256             r#"
257 struct S {}
258 impl S {
259     fn foo(&self, x: i32) {}
260 }
261 fn bar(s: &S) {
262     s.foo(${1:x})$0
263 }
264 "#,
265         );
266 
267         check_edit(
268             "foo",
269             r#"
270 struct S {}
271 impl S {
272     fn foo(&self, x: i32) {
273         $0
274     }
275 }
276 "#,
277             r#"
278 struct S {}
279 impl S {
280     fn foo(&self, x: i32) {
281         self.foo(${1:x})$0
282     }
283 }
284 "#,
285         );
286     }
287 
288     #[test]
parens_for_method_call_as_assoc_fn()289     fn parens_for_method_call_as_assoc_fn() {
290         check_edit(
291             "foo",
292             r#"
293 struct S;
294 impl S {
295     fn foo(&self) {}
296 }
297 fn main() { S::f$0 }
298 "#,
299             r#"
300 struct S;
301 impl S {
302     fn foo(&self) {}
303 }
304 fn main() { S::foo(${1:&self})$0 }
305 "#,
306         );
307     }
308 
309     #[test]
suppress_arg_snippets()310     fn suppress_arg_snippets() {
311         cov_mark::check!(suppress_arg_snippets);
312         check_edit_with_config(
313             CompletionConfig { add_call_argument_snippets: false, ..TEST_CONFIG },
314             "with_args",
315             r#"
316 fn with_args(x: i32, y: String) {}
317 fn main() { with_$0 }
318 "#,
319             r#"
320 fn with_args(x: i32, y: String) {}
321 fn main() { with_args($0) }
322 "#,
323         );
324     }
325 
326     #[test]
strips_underscores_from_args()327     fn strips_underscores_from_args() {
328         check_edit(
329             "foo",
330             r#"
331 fn foo(_foo: i32, ___bar: bool, ho_ge_: String) {}
332 fn main() { f$0 }
333 "#,
334             r#"
335 fn foo(_foo: i32, ___bar: bool, ho_ge_: String) {}
336 fn main() { foo(${1:foo}, ${2:bar}, ${3:ho_ge_})$0 }
337 "#,
338         );
339     }
340 
341     #[test]
insert_ref_when_matching_local_in_scope()342     fn insert_ref_when_matching_local_in_scope() {
343         check_edit(
344             "ref_arg",
345             r#"
346 struct Foo {}
347 fn ref_arg(x: &Foo) {}
348 fn main() {
349     let x = Foo {};
350     ref_ar$0
351 }
352 "#,
353             r#"
354 struct Foo {}
355 fn ref_arg(x: &Foo) {}
356 fn main() {
357     let x = Foo {};
358     ref_arg(${1:&x})$0
359 }
360 "#,
361         );
362     }
363 
364     #[test]
insert_mut_ref_when_matching_local_in_scope()365     fn insert_mut_ref_when_matching_local_in_scope() {
366         check_edit(
367             "ref_arg",
368             r#"
369 struct Foo {}
370 fn ref_arg(x: &mut Foo) {}
371 fn main() {
372     let x = Foo {};
373     ref_ar$0
374 }
375 "#,
376             r#"
377 struct Foo {}
378 fn ref_arg(x: &mut Foo) {}
379 fn main() {
380     let x = Foo {};
381     ref_arg(${1:&mut x})$0
382 }
383 "#,
384         );
385     }
386 
387     #[test]
insert_ref_when_matching_local_in_scope_for_method()388     fn insert_ref_when_matching_local_in_scope_for_method() {
389         check_edit(
390             "apply_foo",
391             r#"
392 struct Foo {}
393 struct Bar {}
394 impl Bar {
395     fn apply_foo(&self, x: &Foo) {}
396 }
397 
398 fn main() {
399     let x = Foo {};
400     let y = Bar {};
401     y.$0
402 }
403 "#,
404             r#"
405 struct Foo {}
406 struct Bar {}
407 impl Bar {
408     fn apply_foo(&self, x: &Foo) {}
409 }
410 
411 fn main() {
412     let x = Foo {};
413     let y = Bar {};
414     y.apply_foo(${1:&x})$0
415 }
416 "#,
417         );
418     }
419 
420     #[test]
trim_mut_keyword_in_func_completion()421     fn trim_mut_keyword_in_func_completion() {
422         check_edit(
423             "take_mutably",
424             r#"
425 fn take_mutably(mut x: &i32) {}
426 
427 fn main() {
428     take_m$0
429 }
430 "#,
431             r#"
432 fn take_mutably(mut x: &i32) {}
433 
434 fn main() {
435     take_mutably(${1:x})$0
436 }
437 "#,
438         );
439     }
440 }
441