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