1use 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 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 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 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 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, ¶m.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) .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 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 if ctx.expected_type.as_ref().is_none_or(|it| it.is_unknown()) && 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 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 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 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 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}