1 //! Renderer for function calls.
3 use hir
::{db::HirDatabase, AsAssocItem, HirDisplay}
;
4 use ide_db
::{SnippetCap, SymbolKind}
;
5 use itertools
::Itertools
;
6 use stdx
::{format_to, to_lower_snake_case}
;
7 use syntax
::{AstNode, SmolStr}
;
10 context
::{CompletionContext, DotAccess, DotAccessKind, PathCompletionCtx, PathKind}
,
11 item
::{Builder, CompletionItem, CompletionItemKind, CompletionRelevance}
,
12 render
::{compute_exact_name_match, compute_ref_match, compute_type_match, RenderContext}
,
18 Function(&'ctx PathCompletionCtx
),
19 Method(&'ctx DotAccess
, Option
<hir
::Name
>),
22 pub(crate) fn render_fn(
23 ctx
: RenderContext
<'_
>,
24 path_ctx
: &PathCompletionCtx
,
25 local_name
: Option
<hir
::Name
>,
28 let _p
= profile
::span("render_fn");
29 render(ctx
, local_name
, func
, FuncKind
::Function(path_ctx
))
32 pub(crate) fn render_method(
33 ctx
: RenderContext
<'_
>,
34 dot_access
: &DotAccess
,
35 receiver
: Option
<hir
::Name
>,
36 local_name
: Option
<hir
::Name
>,
39 let _p
= profile
::span("render_method");
40 render(ctx
, local_name
, func
, FuncKind
::Method(dot_access
, receiver
))
44 ctx @ RenderContext { completion, .. }
: RenderContext
<'_
>,
45 local_name
: Option
<hir
::Name
>,
47 func_kind
: FuncKind
<'_
>,
49 let db
= completion
.db
;
51 let name
= local_name
.unwrap_or_else(|| func
.name(db
));
53 let (call
, escaped_call
) = match &func_kind
{
54 FuncKind
::Method(_
, Some(receiver
)) => (
55 format
!("{}.{}", receiver
.unescaped(), name
.unescaped()).into(),
56 format
!("{}.{}", receiver
, name
).into(),
58 _
=> (name
.unescaped().to_smol_str(), name
.to_smol_str()),
60 let mut item
= CompletionItem
::new(
61 if func
.self_param(db
).is_some() {
62 CompletionItemKind
::Method
64 CompletionItemKind
::SymbolKind(SymbolKind
::Function
)
70 let ret_type
= func
.ret_type(db
);
71 let is_op_method
= func
72 .as_assoc_item(ctx
.db())
73 .and_then(|trait_
| trait_
.containing_trait_or_trait_impl(ctx
.db()))
74 .map_or(false, |trait_
| completion
.is_ops_trait(trait_
));
75 item
.set_relevance(CompletionRelevance
{
76 type_match
: compute_type_match(completion
, &ret_type
),
77 exact_name_match
: compute_exact_name_match(completion
, &call
),
79 ..ctx
.completion_relevance()
83 FuncKind
::Function(path_ctx
) => {
84 super::path_ref_match(completion
, path_ctx
, &ret_type
, &mut item
);
86 FuncKind
::Method(DotAccess { receiver: Some(receiver), .. }
, _
) => {
87 if let Some(original_expr
) = completion
.sema
.original_ast_node(receiver
.clone()) {
88 if let Some(ref_match
) = compute_ref_match(completion
, &ret_type
) {
89 item
.ref_match(ref_match
, original_expr
.syntax().text_range().start());
96 item
.set_documentation(ctx
.docs(func
))
97 .set_deprecated(ctx
.is_deprecated(func
) || ctx
.is_deprecated_assoc_item(func
))
98 .detail(detail(db
, func
))
99 .lookup_by(name
.unescaped().to_smol_str());
101 match ctx
.completion
.config
.snippet_cap
{
103 let complete_params
= match func_kind
{
104 FuncKind
::Function(PathCompletionCtx
{
105 kind
: PathKind
::Expr { .. }
,
106 has_call_parens
: false,
112 DotAccessKind
::Method { has_parens: false }
| DotAccessKind
::Field { .. }
,
119 if let Some(has_dot_receiver
) = complete_params
{
120 if let Some((self_param
, params
)) =
121 params(ctx
.completion
, func
, &func_kind
, has_dot_receiver
)
138 match ctx
.import_to_add
{
139 Some(import_to_add
) => {
140 item
.add_import(import_to_add
);
143 if let Some(actm
) = func
.as_assoc_item(db
) {
144 if let Some(trt
) = actm
.containing_trait_or_trait_impl(db
) {
145 item
.trait_name(trt
.name(db
).to_smol_str());
153 pub(super) fn add_call_parens
<'b
>(
154 builder
: &'b
mut Builder
,
155 ctx
: &CompletionContext
<'_
>,
158 escaped_name
: SmolStr
,
159 self_param
: Option
<hir
::SelfParam
>,
160 params
: Vec
<hir
::Param
>,
161 ) -> &'b
mut Builder
{
162 cov_mark
::hit
!(inserts_parens_for_function_calls
);
164 let (snippet
, label_suffix
) = if self_param
.is_none() && params
.is_empty() {
165 (format
!("{}()$0", escaped_name
), "()")
167 builder
.trigger_call_info();
168 let snippet
= if let Some(CallableSnippets
::FillArguments
) = ctx
.config
.callable
{
169 let offset
= if self_param
.is_some() { 2 }
else { 1 }
;
170 let function_params_snippet
=
171 params
.iter().enumerate().format_with(", ", |(index
, param
), f
| {
172 match param
.name(ctx
.db
) {
174 let smol_str
= n
.to_smol_str();
175 let text
= smol_str
.as_str().trim_start_matches('_'
);
176 let ref_
= ref_of_param(ctx
, text
, param
.ty());
177 f(&format_args
!("${{{}:{}{}}}", index
+ offset
, ref_
, text
))
180 let name
= match param
.ty().as_adt() {
181 None
=> "_".to_string(),
185 .map(|s
| to_lower_snake_case(s
.as_str()))
186 .unwrap_or_else(|| "_".to_string()),
188 f(&format_args
!("${{{}:{}}}", index
+ offset
, name
))
193 Some(self_param
) => {
195 "{}(${{1:{}}}{}{})$0",
197 self_param
.display(ctx
.db
),
198 if params
.is_empty() { "" }
else { ", " }
,
199 function_params_snippet
203 format
!("{}({})$0", escaped_name
, function_params_snippet
)
207 cov_mark
::hit
!(suppress_arg_snippets
);
208 format
!("{}($0)", escaped_name
)
213 builder
.label(SmolStr
::from_iter([&name
, label_suffix
])).insert_snippet(cap
, snippet
)
216 fn ref_of_param(ctx
: &CompletionContext
<'_
>, arg
: &str, ty
: &hir
::Type
) -> &'
static str {
217 if let Some(derefed_ty
) = ty
.remove_ref() {
218 for (name
, local
) in ctx
.locals
.iter() {
219 if name
.as_text().as_deref() == Some(arg
) {
220 return if local
.ty(ctx
.db
) == derefed_ty
{
221 if ty
.is_mutable_reference() {
235 fn detail(db
: &dyn HirDatabase
, func
: hir
::Function
) -> String
{
236 let mut ret_ty
= func
.ret_type(db
);
237 let mut detail
= String
::new();
239 if func
.is_const(db
) {
240 format_to
!(detail
, "const ");
242 if func
.is_async(db
) {
243 format_to
!(detail
, "async ");
244 if let Some(async_ret
) = func
.async_ret_type(db
) {
248 if func
.is_unsafe_to_call(db
) {
249 format_to
!(detail
, "unsafe ");
252 format_to
!(detail
, "fn({})", params_display(db
, func
));
253 if !ret_ty
.is_unit() {
254 format_to
!(detail
, " -> {}", ret_ty
.display(db
));
259 fn params_display(db
: &dyn HirDatabase
, func
: hir
::Function
) -> String
{
260 if let Some(self_param
) = func
.self_param(db
) {
261 let assoc_fn_params
= func
.assoc_fn_params(db
);
262 let params
= assoc_fn_params
264 .skip(1) // skip the self param because we are manually handling that
265 .map(|p
| p
.ty().display(db
));
268 self_param
.display(db
),
269 params
.format_with("", |display
, f
| {
275 let assoc_fn_params
= func
.assoc_fn_params(db
);
276 assoc_fn_params
.iter().map(|p
| p
.ty().display(db
)).join(", ")
281 ctx
: &CompletionContext
<'_
>,
283 func_kind
: &FuncKind
<'_
>,
284 has_dot_receiver
: bool
,
285 ) -> Option
<(Option
<hir
::SelfParam
>, Vec
<hir
::Param
>)> {
286 if ctx
.config
.callable
.is_none() {
290 // Don't add parentheses if the expected type is some function reference.
291 if let Some(ty
) = &ctx
.expected_type
{
292 // FIXME: check signature matches?
294 cov_mark
::hit
!(no_call_parens_if_fn_ptr_needed
);
299 let self_param
= if has_dot_receiver
|| matches
!(func_kind
, FuncKind
::Method(_
, Some(_
))) {
302 func
.self_param(ctx
.db
)
304 Some((self_param
, func
.params_without_self(ctx
.db
)))
310 tests
::{check_edit, check_edit_with_config, TEST_CONFIG}
,
311 CallableSnippets
, CompletionConfig
,
315 fn inserts_parens_for_function_calls() {
316 cov_mark
::check
!(inserts_parens_for_function_calls
);
325 fn main() { no_args()$0 }
332 fn with_args(x: i32, y: String) {}
333 fn main() { with_$0 }
336 fn with_args(x: i32, y: String) {}
337 fn main() { with_args(${1:x}, ${2:y})$0 }
348 fn bar(s: &S) { s.f$0 }
355 fn bar(s: &S) { s.foo()$0 }
364 fn foo(&self, x: i32) {}
373 fn foo(&self, x: i32) {}
386 fn foo(&self, x: i32) {
394 fn foo(&self, x: i32) {
403 fn parens_for_method_call_as_assoc_fn() {
418 fn main() { S::foo(${1:&self})$0 }
424 fn suppress_arg_snippets() {
425 cov_mark
::check
!(suppress_arg_snippets
);
426 check_edit_with_config(
427 CompletionConfig { callable: Some(CallableSnippets::AddParentheses), ..TEST_CONFIG }
,
430 fn with_args(x: i32, y: String) {}
431 fn main() { with_$0 }
434 fn with_args(x: i32, y: String) {}
435 fn main() { with_args($0) }
441 fn strips_underscores_from_args() {
445 fn foo(_foo: i32, ___bar: bool, ho_ge_: String) {}
449 fn foo(_foo: i32, ___bar: bool, ho_ge_: String) {}
450 fn main() { foo(${1:foo}, ${2:bar}, ${3:ho_ge_})$0 }
456 fn insert_ref_when_matching_local_in_scope() {
461 fn ref_arg(x: &Foo) {}
469 fn ref_arg(x: &Foo) {}
479 fn insert_mut_ref_when_matching_local_in_scope() {
484 fn ref_arg(x: &mut Foo) {}
492 fn ref_arg(x: &mut Foo) {}
495 ref_arg(${1:&mut x})$0
502 fn insert_ref_when_matching_local_in_scope_for_method() {
509 fn apply_foo(&self, x: &Foo) {}
522 fn apply_foo(&self, x: &Foo) {}
528 y.apply_foo(${1:&x})$0
535 fn trim_mut_keyword_in_func_completion() {
539 fn take_mutably(mut x: &i32) {}
546 fn take_mutably(mut x: &i32) {}
549 take_mutably(${1:x})$0
556 fn complete_pattern_args_with_type_name_if_adt() {
564 fn qux(Foo { bar }: Foo) {
577 fn qux(Foo { bar }: Foo) {
589 fn complete_fn_param() {
594 fn f(foo: (), mut bar: u32) {}
595 fn g(foo: (), mut ba$0)
598 fn f(foo: (), mut bar: u32) {}
599 fn g(foo: (), mut bar: u32)
607 fn g(foo: (), mut ba$0: u32)
608 fn f(foo: (), mut bar: u32) {}
611 fn g(foo: (), mut bar: u32)
612 fn f(foo: (), mut bar: u32) {}
618 fn complete_fn_mut_param_add_comma() {
619 // add leading and trailing comma
623 fn f(foo: (), mut bar: u32) {}
624 fn g(foo: ()mut ba$0 baz: ())
627 fn f(foo: (), mut bar: u32) {}
628 fn g(foo: (), mut bar: u32, baz: ())
634 fn complete_fn_mut_param_has_attribute() {
636 r
#"#[baz = "qux"] mut bar: u32"#,
638 fn f(foo: (), #[baz = "qux"] mut bar: u32) {}
639 fn g(foo: (), mut ba$0)
642 fn f(foo: (), #[baz = "qux"] mut bar: u32) {}
643 fn g(foo: (), #[baz = "qux"] mut bar: u32)
648 r
#"#[baz = "qux"] mut bar: u32"#,
650 fn f(foo: (), #[baz = "qux"] mut bar: u32) {}
651 fn g(foo: (), #[baz = "qux"] mut ba$0)
654 fn f(foo: (), #[baz = "qux"] mut bar: u32) {}
655 fn g(foo: (), #[baz = "qux"] mut bar: u32)
660 r
#", #[baz = "qux"] mut bar: u32"#,
662 fn f(foo: (), #[baz = "qux"] mut bar: u32) {}
663 fn g(foo: ()#[baz = "qux"] mut ba$0)
666 fn f(foo: (), #[baz = "qux"] mut bar: u32) {}
667 fn g(foo: (), #[baz = "qux"] mut bar: u32)