Skip to main content

ide_completion/render/
function.rs

1//! Renderer for function calls.
2
3use hir::{AsAssocItem, HirDisplay, db::HirDatabase};
4use ide_db::{SnippetCap, SymbolKind};
5use itertools::Itertools;
6use stdx::{format_to, to_lower_snake_case};
7use syntax::{AstNode, SmolStr, ToSmolStr, format_smolstr};
8
9use crate::{
10    CallableSnippets,
11    context::{
12        CompleteSemicolon, CompletionContext, DotAccess, DotAccessKind, PathCompletionCtx, PathKind,
13    },
14    item::{
15        Builder, CompletionItem, CompletionItemKind, CompletionRelevance, CompletionRelevanceFn,
16        CompletionRelevanceReturnType, CompletionRelevanceTraitInfo,
17    },
18    render::{
19        RenderContext, compute_exact_name_match, compute_ref_match, compute_type_match, match_types,
20    },
21};
22
23#[derive(Debug)]
24enum FuncKind<'ctx> {
25    Function(&'ctx PathCompletionCtx<'ctx>),
26    Method(&'ctx DotAccess<'ctx>, Option<SmolStr>),
27}
28
29pub(crate) fn render_fn(
30    ctx: RenderContext<'_, '_>,
31    path_ctx: &PathCompletionCtx<'_>,
32    local_name: Option<hir::Name>,
33    func: hir::Function,
34) -> Builder {
35    let _p = tracing::info_span!("render_fn").entered();
36    render(ctx, local_name, func, FuncKind::Function(path_ctx))
37}
38
39pub(crate) fn render_method(
40    ctx: RenderContext<'_, '_>,
41    dot_access: &DotAccess<'_>,
42    receiver: Option<SmolStr>,
43    local_name: Option<hir::Name>,
44    func: hir::Function,
45) -> Builder {
46    let _p = tracing::info_span!("render_method").entered();
47    render(ctx, local_name, func, FuncKind::Method(dot_access, receiver))
48}
49
50fn render(
51    ctx @ RenderContext { completion, .. }: RenderContext<'_, '_>,
52    local_name: Option<hir::Name>,
53    func: hir::Function,
54    func_kind: FuncKind<'_>,
55) -> Builder {
56    let db = completion.db;
57
58    let name = local_name.unwrap_or_else(|| func.name(db));
59
60    let (call, escaped_call) = match &func_kind {
61        FuncKind::Method(_, Some(receiver)) => (
62            format_smolstr!("{}.{}", receiver, name.as_str()),
63            format_smolstr!("{}.{}", receiver, name.display(ctx.db(), completion.edition)),
64        ),
65        _ => (name.as_str().to_smolstr(), name.display(db, completion.edition).to_smolstr()),
66    };
67    let has_self_param = func.self_param(db).is_some();
68    let mut item = CompletionItem::new(
69        CompletionItemKind::SymbolKind(if has_self_param {
70            SymbolKind::Method
71        } else {
72            SymbolKind::Function
73        }),
74        ctx.source_range(),
75        call.clone(),
76        completion.edition,
77    );
78
79    let ret_type = ctx.completion.rebase_ty(&func.ret_type(db));
80    let assoc_item = func.as_assoc_item(db);
81
82    let trait_info =
83        assoc_item.and_then(|trait_| trait_.container_or_implemented_trait(db)).map(|trait_| {
84            CompletionRelevanceTraitInfo {
85                notable_trait: completion.is_doc_notable_trait(trait_),
86                is_op_method: completion.is_ops_trait(trait_),
87            }
88        });
89
90    let (has_dot_receiver, has_call_parens, cap) = match func_kind {
91        FuncKind::Function(&PathCompletionCtx {
92            kind: PathKind::Expr { .. },
93            has_call_parens,
94            ..
95        }) => (false, has_call_parens, ctx.completion.config.snippet_cap),
96        FuncKind::Method(&DotAccess { kind: DotAccessKind::Method, .. }, _) => {
97            (true, true, ctx.completion.config.snippet_cap)
98        }
99        FuncKind::Method(DotAccess { kind: DotAccessKind::Field { .. }, .. }, _) => {
100            (true, false, ctx.completion.config.snippet_cap)
101        }
102        _ => (false, false, None),
103    };
104    let complete_call_parens = cap.filter(|_| !has_call_parens).and_then(|cap| {
105        Some((cap, params(ctx.completion, func, &func_kind, has_dot_receiver, &ret_type)?))
106    });
107
108    let function = assoc_item
109        .and_then(|assoc_item| assoc_item.implementing_ty(db))
110        .map(|self_type| ctx.completion.rebase_ty(&self_type))
111        .map(|self_type| compute_return_type_match(db, &ctx, self_type, &ret_type))
112        .map(|return_type| CompletionRelevanceFn {
113            has_params: has_self_param || func.num_params(db) > 0,
114            has_self_param,
115            return_type,
116        });
117
118    item.set_relevance(CompletionRelevance {
119        type_match: if has_call_parens || complete_call_parens.is_some() {
120            compute_type_match(completion, &ret_type)
121        } else {
122            compute_type_match(completion, &ctx.completion.rebase_ty(&func.ty(db)))
123        },
124        exact_name_match: compute_exact_name_match(completion, name.as_str()),
125        function,
126        trait_: trait_info,
127        is_skipping_completion: matches!(func_kind, FuncKind::Method(_, Some(_))),
128        ..ctx.completion_relevance()
129    });
130
131    match func_kind {
132        FuncKind::Function(path_ctx) => {
133            super::path_ref_match(completion, path_ctx, &ret_type, &mut item);
134        }
135        FuncKind::Method(DotAccess { receiver: Some(receiver), .. }, _) => {
136            if let Some(original_expr) = completion.sema.original_range_opt(receiver.syntax())
137                && let Some(ref_mode) = compute_ref_match(completion, &ret_type)
138            {
139                item.ref_match(ref_mode, original_expr.range.start());
140            }
141        }
142        _ => (),
143    }
144
145    let detail = if ctx.completion.config.full_function_signatures {
146        detail_full(ctx.completion, func)
147    } else {
148        detail(ctx.completion, func)
149    };
150    item.set_documentation(ctx.docs(func))
151        .set_deprecated(ctx.is_deprecated(func, func.as_assoc_item(db)))
152        .detail(detail)
153        .lookup_by(name.as_str().to_smolstr());
154
155    if let Some((cap, (self_param, params))) = complete_call_parens {
156        add_call_parens(
157            &mut item,
158            completion,
159            cap,
160            call,
161            escaped_call,
162            self_param,
163            params,
164            &ret_type,
165            func,
166        );
167    }
168
169    match ctx.import_to_add {
170        Some(import_to_add) => {
171            item.add_import(import_to_add);
172        }
173        None => {
174            if let Some(actm) = assoc_item
175                && let Some(trt) = actm.container_or_implemented_trait(db)
176            {
177                item.trait_name(trt.name(db).display_no_db(ctx.completion.edition).to_smolstr());
178            }
179        }
180    }
181
182    item.doc_aliases(ctx.doc_aliases);
183    item
184}
185
186fn compute_return_type_match(
187    db: &dyn HirDatabase,
188    ctx: &RenderContext<'_, '_>,
189    self_type: hir::Type<'_>,
190    ret_type: &hir::Type<'_>,
191) -> CompletionRelevanceReturnType {
192    if match_types(ctx.completion, &self_type, ret_type).is_some() {
193        // fn([..]) -> Self
194        CompletionRelevanceReturnType::DirectConstructor
195    } else if ret_type
196        .type_arguments()
197        .any(|ret_type_arg| match_types(ctx.completion, &self_type, &ret_type_arg).is_some())
198    {
199        // fn([..]) -> Result<Self, E> OR Wrapped<Foo, Self>
200        CompletionRelevanceReturnType::Constructor
201    } else if ret_type
202        .as_adt()
203        .map(|adt| adt.name(db).as_str().ends_with("Builder"))
204        .unwrap_or(false)
205    {
206        // fn([..]) -> [..]Builder
207        CompletionRelevanceReturnType::Builder
208    } else {
209        CompletionRelevanceReturnType::Other
210    }
211}
212
213pub(super) fn add_call_parens<'b>(
214    builder: &'b mut Builder,
215    ctx: &CompletionContext<'_, '_>,
216    cap: SnippetCap,
217    name: SmolStr,
218    escaped_name: SmolStr,
219    self_param: Option<hir::SelfParam>,
220    params: Vec<hir::Param<'_>>,
221    ret_type: &hir::Type<'_>,
222    func: hir::Function,
223) -> &'b mut Builder {
224    cov_mark::hit!(inserts_parens_for_function_calls);
225
226    let (mut snippet, label_suffix) = if self_param.is_none() && params.is_empty() {
227        let snippet = if ctx.config.enable_turbofish
228            && let gen_params = hir::GenericDef::Function(func).type_or_const_params(ctx.db)
229            && gen_params.iter().any(|it| it.as_type_param(ctx.db).is_some())
230            && !ret_type.contains_unknown()
231        {
232            // Return type not contained any generic param, so can't infer param
233            // `fn size_of<T>() -> usize` -> `size_of::<$1>()$0`
234            format!("{escaped_name}::<$1>()$0")
235        } else {
236            format!("{escaped_name}()$0")
237        };
238        (snippet, "()")
239    } else {
240        builder.trigger_call_info();
241        let snippet = if let Some(CallableSnippets::FillArguments) = ctx.config.callable {
242            let offset = if self_param.is_some() { 2 } else { 1 };
243            let function_params_snippet =
244                params.iter().enumerate().format_with(", ", |(index, param), f| {
245                    match param.name(ctx.db) {
246                        Some(n) => {
247                            let smol_str = n.display_no_db(ctx.edition).to_smolstr();
248                            let text = smol_str.as_str().trim_start_matches('_');
249                            let ref_ =
250                                ref_of_param(ctx, text, &param.ty().instantiate_with_errors());
251                            f(&format_args!("${{{}:{ref_}{text}}}", index + offset))
252                        }
253                        None => {
254                            let name = match param.ty().as_adt() {
255                                None => "_".to_owned(),
256                                Some(adt) => to_lower_snake_case(adt.name(ctx.db).as_str()),
257                            };
258                            f(&format_args!("${{{}:{name}}}", index + offset))
259                        }
260                    }
261                });
262            match self_param {
263                Some(self_param) => {
264                    format!(
265                        "{}(${{1:{}}}{}{})$0",
266                        escaped_name,
267                        self_param.display(ctx.db, ctx.display_target),
268                        if params.is_empty() { "" } else { ", " },
269                        function_params_snippet
270                    )
271                }
272                None => {
273                    format!("{escaped_name}({function_params_snippet})$0")
274                }
275            }
276        } else {
277            cov_mark::hit!(suppress_arg_snippets);
278            format!("{escaped_name}($0)")
279        };
280
281        (snippet, "(…)")
282    };
283    if ret_type.is_unit() {
284        match ctx.complete_semicolon {
285            CompleteSemicolon::DoNotComplete => {}
286            CompleteSemicolon::CompleteSemi | CompleteSemicolon::CompleteComma => {
287                cov_mark::hit!(complete_semicolon);
288                let ch = if matches!(ctx.complete_semicolon, CompleteSemicolon::CompleteComma) {
289                    ','
290                } else {
291                    ';'
292                };
293                if snippet.ends_with("$0") {
294                    snippet.insert(snippet.len() - "$0".len(), ch);
295                } else {
296                    snippet.push(ch);
297                }
298            }
299        }
300    }
301    builder.label(SmolStr::from_iter([&name, label_suffix])).insert_snippet(cap, snippet)
302}
303
304fn ref_of_param(ctx: &CompletionContext<'_, '_>, arg: &str, ty: &hir::Type<'_>) -> &'static str {
305    if let Some((_, mutability)) = ty.as_reference() {
306        let ref_prefix = if mutability.is_mut() { "&mut " } else { "&" };
307
308        for (name, local) in ctx.locals.iter().sorted_by_key(|&(k, _)| k.clone()) {
309            if name.as_str() == arg {
310                let local_ty = local.ty(ctx.db);
311                let added_ref = local_ty.add_reference(ctx.db, mutability);
312                let needs_ref =
313                    !local_ty.could_coerce_to(ctx.db, ty) && added_ref.could_coerce_to(ctx.db, ty);
314                return if needs_ref { ref_prefix } else { "" };
315            }
316        }
317    }
318    ""
319}
320
321fn detail(ctx: &CompletionContext<'_, '_>, func: hir::Function) -> String {
322    let mut ret_ty = func.ret_type(ctx.db);
323    let mut detail = String::new();
324
325    if func.is_const(ctx.db) {
326        format_to!(detail, "const ");
327    }
328    if func.is_async(ctx.db) {
329        format_to!(detail, "async ");
330        if let Some(async_ret) = func.async_ret_type(ctx.db) {
331            ret_ty = async_ret;
332        }
333    }
334    if func.is_unsafe_to_call(ctx.db, ctx.containing_function, ctx.edition) {
335        format_to!(detail, "unsafe ");
336    }
337
338    detail.push_str("fn(");
339    params_display(ctx, &mut detail, func);
340    detail.push(')');
341    if !ret_ty.is_unit() {
342        format_to!(detail, " -> {}", ret_ty.display(ctx.db, ctx.display_target));
343    }
344    detail
345}
346
347fn detail_full(ctx: &CompletionContext<'_, '_>, func: hir::Function) -> String {
348    let signature = format!("{}", func.display(ctx.db, ctx.display_target));
349    let mut detail = String::with_capacity(signature.len());
350
351    for segment in signature.split_whitespace() {
352        if !detail.is_empty() && !detail.ends_with('(') && !segment.starts_with(')') {
353            detail.push(' ');
354        }
355
356        detail.push_str(segment);
357    }
358
359    detail
360}
361
362fn params_display(ctx: &CompletionContext<'_, '_>, detail: &mut String, func: hir::Function) {
363    if let Some(self_param) = func.self_param(ctx.db) {
364        format_to!(detail, "{}", self_param.display(ctx.db, ctx.display_target));
365        let assoc_fn_params = func.assoc_fn_params(ctx.db);
366        let params = assoc_fn_params
367            .iter()
368            .skip(1) // skip the self param because we are manually handling that
369            .map(|p| p.ty().display(ctx.db, ctx.display_target));
370        for param in params {
371            format_to!(detail, ", {}", param);
372        }
373    } else {
374        let assoc_fn_params = func.assoc_fn_params(ctx.db);
375        format_to!(
376            detail,
377            "{}",
378            assoc_fn_params.iter().map(|p| p.ty().display(ctx.db, ctx.display_target)).format(", ")
379        );
380    }
381
382    if func.is_varargs(ctx.db) {
383        detail.push_str(", ...");
384    }
385}
386
387fn params<'db>(
388    ctx: &CompletionContext<'_, 'db>,
389    func: hir::Function,
390    func_kind: &FuncKind<'_>,
391    has_dot_receiver: bool,
392    ret_type: &hir::Type<'_>,
393) -> Option<(Option<hir::SelfParam>, Vec<hir::Param<'db>>)> {
394    ctx.config.callable.as_ref()?;
395
396    // Don't add parentheses if the expected type is a function reference with the same signature.
397    if let Some(expected) = ctx.expected_type.as_ref().filter(|e| e.is_fn())
398        && let Some(expected) = expected.as_callable(ctx.db)
399        && let Some(completed) = func.ty(ctx.db).as_callable(ctx.db)
400        && expected.sig() == completed.sig()
401    {
402        cov_mark::hit!(no_call_parens_if_fn_ptr_needed);
403        return None;
404    }
405    // Handle generic signature, do not use FnSig::eq
406    if ctx.expected_type.as_ref().is_none_or(|it| it.is_unknown()) // only process generic expected
407        && let Some(expected) = &ctx.expected_func
408        && let Some(completed) = func.ty(ctx.db).as_callable(ctx.db)
409        && expected.n_params() == completed.n_params()
410        && ctx.rebase_ty(&expected.return_type()).could_unify_with(ctx.db, ret_type)
411        && !completed.return_type().impls_fnonce(ctx.db)
412    {
413        cov_mark::hit!(no_call_parens_if_ty_with_fn_bounds);
414        return None;
415    }
416
417    let self_param = if has_dot_receiver || matches!(func_kind, FuncKind::Method(_, Some(_))) {
418        None
419    } else {
420        func.self_param(ctx.db)
421    };
422    Some((self_param, func.params_without_self(ctx.db)))
423}
424
425#[cfg(test)]
426mod tests {
427    use crate::{
428        CallableSnippets, CompletionConfig,
429        tests::{TEST_CONFIG, check_edit, check_edit_with_config},
430    };
431
432    #[test]
433    fn inserts_parens_for_function_calls() {
434        cov_mark::check!(inserts_parens_for_function_calls);
435        check_edit(
436            "no_args",
437            r#"
438fn no_args() {}
439fn main() { no_$0 }
440"#,
441            r#"
442fn no_args() {}
443fn main() { no_args();$0 }
444"#,
445        );
446
447        check_edit(
448            "with_args",
449            r#"
450fn with_args(x: i32, y: String) {}
451fn main() { with_$0 }
452"#,
453            r#"
454fn with_args(x: i32, y: String) {}
455fn main() { with_args(${1:x}, ${2:y});$0 }
456"#,
457        );
458
459        check_edit(
460            "foo",
461            r#"
462struct S;
463impl S {
464    fn foo(&self) -> i32 { 0 }
465}
466fn bar(s: &S) { s.f$0 }
467"#,
468            r#"
469struct S;
470impl S {
471    fn foo(&self) -> i32 { 0 }
472}
473fn bar(s: &S) { s.foo()$0 }
474"#,
475        );
476
477        check_edit(
478            "foo",
479            r#"
480struct S {}
481impl S {
482    fn foo(&self, x: i32) {}
483}
484fn bar(s: &S) {
485    s.f$0
486}
487"#,
488            r#"
489struct S {}
490impl S {
491    fn foo(&self, x: i32) {}
492}
493fn bar(s: &S) {
494    s.foo(${1:x});$0
495}
496"#,
497        );
498
499        check_edit(
500            "foo",
501            r#"
502struct S {}
503impl S {
504    fn foo(&self, x: i32, y: &i32) {
505        $0
506    }
507}
508"#,
509            r#"
510struct S {}
511impl S {
512    fn foo(&self, x: i32, y: &i32) {
513        self.foo(${1:x}, ${2:y});$0
514    }
515}
516"#,
517        );
518    }
519
520    #[test]
521    fn parens_for_method_call_as_assoc_fn() {
522        check_edit(
523            "foo",
524            r#"
525struct S;
526impl S {
527    fn foo(&self) {}
528}
529fn main() { S::f$0 }
530"#,
531            r#"
532struct S;
533impl S {
534    fn foo(&self) {}
535}
536fn main() { S::foo(${1:&self});$0 }
537"#,
538        );
539    }
540
541    #[test]
542    fn suppress_arg_snippets() {
543        cov_mark::check!(suppress_arg_snippets);
544        check_edit_with_config(
545            CompletionConfig { callable: Some(CallableSnippets::AddParentheses), ..TEST_CONFIG },
546            "with_args",
547            r#"
548fn with_args(x: i32, y: String) {}
549fn main() { with_$0 }
550"#,
551            r#"
552fn with_args(x: i32, y: String) {}
553fn main() { with_args($0); }
554"#,
555        );
556    }
557
558    #[test]
559    fn strips_underscores_from_args() {
560        check_edit(
561            "foo",
562            r#"
563fn foo(_foo: i32, ___bar: bool, ho_ge_: String) {}
564fn main() { f$0 }
565"#,
566            r#"
567fn foo(_foo: i32, ___bar: bool, ho_ge_: String) {}
568fn main() { foo(${1:foo}, ${2:bar}, ${3:ho_ge_});$0 }
569"#,
570        );
571    }
572
573    #[test]
574    fn insert_ref_when_matching_local_in_scope() {
575        check_edit(
576            "ref_arg",
577            r#"
578struct Foo {}
579fn ref_arg(x: &Foo) {}
580fn main() {
581    let x = Foo {};
582    ref_ar$0
583}
584"#,
585            r#"
586struct Foo {}
587fn ref_arg(x: &Foo) {}
588fn main() {
589    let x = Foo {};
590    ref_arg(${1:&x});$0
591}
592"#,
593        );
594        check_edit(
595            "ref_arg",
596            r#"
597//- minicore: coerce_unsized
598fn ref_arg(x: &[i32]) {}
599fn main() {
600    let x = [2];
601    ref_ar$0
602}
603"#,
604            r#"
605fn ref_arg(x: &[i32]) {}
606fn main() {
607    let x = [2];
608    ref_arg(${1:&x});$0
609}
610"#,
611        );
612    }
613
614    #[test]
615    fn insert_mut_ref_when_matching_local_in_scope() {
616        check_edit(
617            "ref_arg",
618            r#"
619struct Foo {}
620fn ref_arg(x: &mut Foo) {}
621fn main() {
622    let x = Foo {};
623    ref_ar$0
624}
625"#,
626            r#"
627struct Foo {}
628fn ref_arg(x: &mut Foo) {}
629fn main() {
630    let x = Foo {};
631    ref_arg(${1:&mut x});$0
632}
633"#,
634        );
635    }
636
637    #[test]
638    fn insert_ref_when_matching_local_in_scope_for_method() {
639        check_edit(
640            "apply_foo",
641            r#"
642struct Foo {}
643struct Bar {}
644impl Bar {
645    fn apply_foo(&self, x: &Foo) {}
646}
647
648fn main() {
649    let x = Foo {};
650    let y = Bar {};
651    y.$0
652}
653"#,
654            r#"
655struct Foo {}
656struct Bar {}
657impl Bar {
658    fn apply_foo(&self, x: &Foo) {}
659}
660
661fn main() {
662    let x = Foo {};
663    let y = Bar {};
664    y.apply_foo(${1:&x});$0
665}
666"#,
667        );
668    }
669
670    #[test]
671    fn trim_mut_keyword_in_func_completion() {
672        check_edit(
673            "take_mutably",
674            r#"
675fn take_mutably(mut x: &i32) {}
676
677fn main() {
678    take_m$0
679}
680"#,
681            r#"
682fn take_mutably(mut x: &i32) {}
683
684fn main() {
685    take_mutably(${1:x});$0
686}
687"#,
688        );
689    }
690
691    #[test]
692    fn complete_pattern_args_with_type_name_if_adt() {
693        check_edit(
694            "qux",
695            r#"
696struct Foo {
697    bar: i32
698}
699
700fn qux(Foo { bar }: Foo) {
701    println!("{}", bar);
702}
703
704fn main() {
705  qu$0
706}
707"#,
708            r#"
709struct Foo {
710    bar: i32
711}
712
713fn qux(Foo { bar }: Foo) {
714    println!("{}", bar);
715}
716
717fn main() {
718  qux(${1:foo});$0
719}
720"#,
721        );
722    }
723
724    #[test]
725    fn complete_fn_param() {
726        // has mut kw
727        check_edit(
728            "bar: u32",
729            r#"
730fn f(foo: (), mut bar: u32) {}
731fn g(foo: (), mut ba$0)
732"#,
733            r#"
734fn f(foo: (), mut bar: u32) {}
735fn g(foo: (), mut bar: u32)
736"#,
737        );
738
739        // has unmatched mut kw
740        check_edit(
741            "bar: u32",
742            r#"
743fn f(foo: (), bar: u32) {}
744fn g(foo: (), mut ba$0)
745"#,
746            r#"
747fn f(foo: (), bar: u32) {}
748fn g(foo: (), mut bar: u32)
749"#,
750        );
751
752        check_edit(
753            "mut bar: u32",
754            r#"
755fn f(foo: (), mut bar: u32) {}
756fn g(foo: (), ba$0)
757"#,
758            r#"
759fn f(foo: (), mut bar: u32) {}
760fn g(foo: (), mut bar: u32)
761"#,
762        );
763
764        // has type param
765        check_edit(
766            "bar: u32",
767            r#"
768fn g(foo: (), mut ba$0: u32)
769fn f(foo: (), mut bar: u32) {}
770"#,
771            r#"
772fn g(foo: (), mut bar: u32)
773fn f(foo: (), mut bar: u32) {}
774"#,
775        );
776    }
777
778    #[test]
779    fn complete_fn_mut_param_add_comma() {
780        // add leading and trailing comma
781        check_edit(
782            "bar: u32",
783            r#"
784fn f(foo: (), mut bar: u32) {}
785fn g(foo: ()mut ba$0 baz: ())
786"#,
787            r#"
788fn f(foo: (), mut bar: u32) {}
789fn g(foo: (), mut bar: u32, baz: ())
790"#,
791        );
792    }
793
794    #[test]
795    fn complete_fn_mut_param_has_attribute() {
796        check_edit(
797            r#"#[baz = "qux"] mut bar: u32"#,
798            r#"
799fn f(foo: (), #[baz = "qux"] mut bar: u32) {}
800fn g(foo: (), mut ba$0)
801"#,
802            r#"
803fn f(foo: (), #[baz = "qux"] mut bar: u32) {}
804fn g(foo: (), #[baz = "qux"] mut bar: u32)
805"#,
806        );
807
808        check_edit(
809            r#"#[baz = "qux"] mut bar: u32"#,
810            r#"
811fn f(foo: (), #[baz = "qux"] mut bar: u32) {}
812fn g(foo: (), #[baz = "qux"] mut ba$0)
813"#,
814            r#"
815fn f(foo: (), #[baz = "qux"] mut bar: u32) {}
816fn g(foo: (), #[baz = "qux"] mut bar: u32)
817"#,
818        );
819
820        check_edit(
821            r#"#[baz = "qux"] mut bar: u32"#,
822            r#"
823fn f(foo: (), #[baz = "qux"] mut bar: u32) {}
824fn g(foo: ()#[baz = "qux"] mut ba$0)
825"#,
826            r#"
827fn f(foo: (), #[baz = "qux"] mut bar: u32) {}
828fn g(foo: (), #[baz = "qux"] mut bar: u32)
829"#,
830        );
831    }
832
833    #[test]
834    fn complete_semicolon_for_unit() {
835        cov_mark::check!(complete_semicolon);
836        check_edit(
837            r#"foo"#,
838            r#"
839fn foo() {}
840fn bar() {
841    foo$0
842}
843"#,
844            r#"
845fn foo() {}
846fn bar() {
847    foo();$0
848}
849"#,
850        );
851        check_edit(
852            r#"foo"#,
853            r#"
854fn foo(a: i32) {}
855fn bar() {
856    foo$0
857}
858"#,
859            r#"
860fn foo(a: i32) {}
861fn bar() {
862    foo(${1:a});$0
863}
864"#,
865        );
866        check_edit(
867            r#"foo"#,
868            r#"
869fn foo(a: i32) {}
870fn bar() {
871    foo$0;
872}
873"#,
874            r#"
875fn foo(a: i32) {}
876fn bar() {
877    foo(${1:a})$0;
878}
879"#,
880        );
881        check_edit_with_config(
882            CompletionConfig { add_semicolon_to_unit: false, ..TEST_CONFIG },
883            r#"foo"#,
884            r#"
885fn foo(a: i32) {}
886fn bar() {
887    foo$0
888}
889"#,
890            r#"
891fn foo(a: i32) {}
892fn bar() {
893    foo(${1:a})$0
894}
895"#,
896        );
897    }
898
899    #[test]
900    fn complete_comma_for_unit_match_arm() {
901        cov_mark::check!(complete_semicolon);
902        check_edit(
903            r#"foo"#,
904            r#"
905fn foo() {}
906fn bar() {
907    match Some(false) {
908        v => fo$0
909    }
910}
911"#,
912            r#"
913fn foo() {}
914fn bar() {
915    match Some(false) {
916        v => foo(),$0
917    }
918}
919"#,
920        );
921        check_edit(
922            r#"foo"#,
923            r#"
924fn foo() {}
925fn bar() {
926    match Some(false) {
927        v => fo$0,
928    }
929}
930"#,
931            r#"
932fn foo() {}
933fn bar() {
934    match Some(false) {
935        v => foo()$0,
936    }
937}
938"#,
939        );
940    }
941
942    #[test]
943    fn no_semicolon_in_closure_ret() {
944        check_edit(
945            r#"foo"#,
946            r#"
947fn foo() {}
948fn baz(_: impl FnOnce()) {}
949fn bar() {
950    baz(|| fo$0);
951}
952"#,
953            r#"
954fn foo() {}
955fn baz(_: impl FnOnce()) {}
956fn bar() {
957    baz(|| foo()$0);
958}
959"#,
960        );
961    }
962
963    #[test]
964    fn no_semicolon_in_arg_list() {
965        check_edit(
966            r#"foo"#,
967            r#"
968fn foo() {}
969fn baz(_: impl FnOnce()) {}
970fn bar() {
971    baz(fo$0);
972}
973"#,
974            r#"
975fn foo() {}
976fn baz(_: impl FnOnce()) {}
977fn bar() {
978    baz(foo()$0);
979}
980"#,
981        );
982    }
983
984    #[test]
985    fn no_semicolon_in_array() {
986        check_edit(
987            r#"foo"#,
988            r#"
989fn foo() {}
990fn bar() {
991    let _ = [fo$0];
992}
993"#,
994            r#"
995fn foo() {}
996fn bar() {
997    let _ = [foo()$0];
998}
999"#,
1000        );
1001    }
1002
1003    #[test]
1004    fn no_semicolon_in_match() {
1005        check_edit(
1006            r#"foo"#,
1007            r#"
1008fn foo() {}
1009fn bar() {
1010    match fo$0 {}
1011}
1012"#,
1013            r#"
1014fn foo() {}
1015fn bar() {
1016    match foo()$0 {}
1017}
1018"#,
1019        );
1020    }
1021
1022    #[test]
1023    fn turbofish() {
1024        check_edit(
1025            "size_of",
1026            r#"
1027//- minicore: size_of
1028fn main() {
1029    si$0
1030}
1031"#,
1032            r#"
1033fn main() {
1034    size_of::<$1>()$0
1035}
1036"#,
1037        );
1038        check_edit_with_config(
1039            CompletionConfig { enable_turbofish: false, ..TEST_CONFIG },
1040            "size_of",
1041            r#"
1042//- minicore: size_of
1043fn main() {
1044    si$0
1045}
1046"#,
1047            r#"
1048fn main() {
1049    size_of()$0
1050}
1051"#,
1052        );
1053        check_edit(
1054            "new",
1055            r#"
1056struct Layout;
1057impl Layout {
1058    pub const fn new<T>() -> Self {
1059        Self
1060    }
1061}
1062fn main() {
1063    Layout::$0
1064}
1065"#,
1066            r#"
1067struct Layout;
1068impl Layout {
1069    pub const fn new<T>() -> Self {
1070        Self
1071    }
1072}
1073fn main() {
1074    Layout::new::<$1>()$0
1075}
1076"#,
1077        );
1078        check_edit(
1079            "handle",
1080            r#"
1081//- minicore: result
1082struct State;
1083enum E { E }
1084impl State {
1085    fn handle<T>(&mut self) -> Result<(), E> {
1086    }
1087}
1088fn main() {
1089    State.$0
1090}
1091"#,
1092            r#"
1093struct State;
1094enum E { E }
1095impl State {
1096    fn handle<T>(&mut self) -> Result<(), E> {
1097    }
1098}
1099fn main() {
1100    State.handle::<$1>()$0
1101}
1102"#,
1103        );
1104    }
1105
1106    #[test]
1107    fn no_turbofish() {
1108        check_edit(
1109            "handle",
1110            r#"
1111//- minicore: result
1112struct State;
1113enum E { E }
1114impl State {
1115    fn handle<T>(&mut self) -> Result<T, E> {
1116    }
1117}
1118fn main() {
1119    State.$0
1120}
1121"#,
1122            r#"
1123struct State;
1124enum E { E }
1125impl State {
1126    fn handle<T>(&mut self) -> Result<T, E> {
1127    }
1128}
1129fn main() {
1130    State.handle()$0
1131}
1132"#,
1133        );
1134    }
1135}