aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_assists/src/handlers/generate_function.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide_assists/src/handlers/generate_function.rs')
-rw-r--r--crates/ide_assists/src/handlers/generate_function.rs1071
1 files changed, 1071 insertions, 0 deletions
diff --git a/crates/ide_assists/src/handlers/generate_function.rs b/crates/ide_assists/src/handlers/generate_function.rs
new file mode 100644
index 000000000..959824981
--- /dev/null
+++ b/crates/ide_assists/src/handlers/generate_function.rs
@@ -0,0 +1,1071 @@
1use hir::HirDisplay;
2use ide_db::{base_db::FileId, helpers::SnippetCap};
3use rustc_hash::{FxHashMap, FxHashSet};
4use syntax::{
5 ast::{
6 self,
7 edit::{AstNodeEdit, IndentLevel},
8 make, ArgListOwner, AstNode, ModuleItemOwner,
9 },
10 SyntaxKind, SyntaxNode, TextSize,
11};
12
13use crate::{
14 utils::{render_snippet, Cursor},
15 AssistContext, AssistId, AssistKind, Assists,
16};
17
18// Assist: generate_function
19//
20// Adds a stub function with a signature matching the function under the cursor.
21//
22// ```
23// struct Baz;
24// fn baz() -> Baz { Baz }
25// fn foo() {
26// bar$0("", baz());
27// }
28//
29// ```
30// ->
31// ```
32// struct Baz;
33// fn baz() -> Baz { Baz }
34// fn foo() {
35// bar("", baz());
36// }
37//
38// fn bar(arg: &str, baz: Baz) ${0:-> ()} {
39// todo!()
40// }
41//
42// ```
43pub(crate) fn generate_function(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
44 let path_expr: ast::PathExpr = ctx.find_node_at_offset()?;
45 let call = path_expr.syntax().parent().and_then(ast::CallExpr::cast)?;
46 let path = path_expr.path()?;
47
48 if ctx.sema.resolve_path(&path).is_some() {
49 // The function call already resolves, no need to add a function
50 return None;
51 }
52
53 let target_module = match path.qualifier() {
54 Some(qualifier) => match ctx.sema.resolve_path(&qualifier) {
55 Some(hir::PathResolution::Def(hir::ModuleDef::Module(module))) => Some(module),
56 _ => return None,
57 },
58 None => None,
59 };
60
61 let function_builder = FunctionBuilder::from_call(&ctx, &call, &path, target_module)?;
62
63 let target = call.syntax().text_range();
64 acc.add(
65 AssistId("generate_function", AssistKind::Generate),
66 format!("Generate `{}` function", function_builder.fn_name),
67 target,
68 |builder| {
69 let function_template = function_builder.render();
70 builder.edit_file(function_template.file);
71 let new_fn = function_template.to_string(ctx.config.snippet_cap);
72 match ctx.config.snippet_cap {
73 Some(cap) => builder.insert_snippet(cap, function_template.insert_offset, new_fn),
74 None => builder.insert(function_template.insert_offset, new_fn),
75 }
76 },
77 )
78}
79
80struct FunctionTemplate {
81 insert_offset: TextSize,
82 leading_ws: String,
83 fn_def: ast::Fn,
84 ret_type: ast::RetType,
85 trailing_ws: String,
86 file: FileId,
87}
88
89impl FunctionTemplate {
90 fn to_string(&self, cap: Option<SnippetCap>) -> String {
91 let f = match cap {
92 Some(cap) => {
93 render_snippet(cap, self.fn_def.syntax(), Cursor::Replace(self.ret_type.syntax()))
94 }
95 None => self.fn_def.to_string(),
96 };
97 format!("{}{}{}", self.leading_ws, f, self.trailing_ws)
98 }
99}
100
101struct FunctionBuilder {
102 target: GeneratedFunctionTarget,
103 fn_name: ast::Name,
104 type_params: Option<ast::GenericParamList>,
105 params: ast::ParamList,
106 file: FileId,
107 needs_pub: bool,
108}
109
110impl FunctionBuilder {
111 /// Prepares a generated function that matches `call`.
112 /// The function is generated in `target_module` or next to `call`
113 fn from_call(
114 ctx: &AssistContext,
115 call: &ast::CallExpr,
116 path: &ast::Path,
117 target_module: Option<hir::Module>,
118 ) -> Option<Self> {
119 let mut file = ctx.frange.file_id;
120 let target = match &target_module {
121 Some(target_module) => {
122 let module_source = target_module.definition_source(ctx.db());
123 let (in_file, target) = next_space_for_fn_in_module(ctx.sema.db, &module_source)?;
124 file = in_file;
125 target
126 }
127 None => next_space_for_fn_after_call_site(&call)?,
128 };
129 let needs_pub = target_module.is_some();
130 let target_module = target_module.or_else(|| ctx.sema.scope(target.syntax()).module())?;
131 let fn_name = fn_name(&path)?;
132 let (type_params, params) = fn_args(ctx, target_module, &call)?;
133
134 Some(Self { target, fn_name, type_params, params, file, needs_pub })
135 }
136
137 fn render(self) -> FunctionTemplate {
138 let placeholder_expr = make::expr_todo();
139 let fn_body = make::block_expr(vec![], Some(placeholder_expr));
140 let visibility = if self.needs_pub { Some(make::visibility_pub_crate()) } else { None };
141 let mut fn_def = make::fn_(
142 visibility,
143 self.fn_name,
144 self.type_params,
145 self.params,
146 fn_body,
147 Some(make::ret_type(make::ty_unit())),
148 );
149 let leading_ws;
150 let trailing_ws;
151
152 let insert_offset = match self.target {
153 GeneratedFunctionTarget::BehindItem(it) => {
154 let indent = IndentLevel::from_node(&it);
155 leading_ws = format!("\n\n{}", indent);
156 fn_def = fn_def.indent(indent);
157 trailing_ws = String::new();
158 it.text_range().end()
159 }
160 GeneratedFunctionTarget::InEmptyItemList(it) => {
161 let indent = IndentLevel::from_node(&it);
162 leading_ws = format!("\n{}", indent + 1);
163 fn_def = fn_def.indent(indent + 1);
164 trailing_ws = format!("\n{}", indent);
165 it.text_range().start() + TextSize::of('{')
166 }
167 };
168
169 FunctionTemplate {
170 insert_offset,
171 leading_ws,
172 ret_type: fn_def.ret_type().unwrap(),
173 fn_def,
174 trailing_ws,
175 file: self.file,
176 }
177 }
178}
179
180enum GeneratedFunctionTarget {
181 BehindItem(SyntaxNode),
182 InEmptyItemList(SyntaxNode),
183}
184
185impl GeneratedFunctionTarget {
186 fn syntax(&self) -> &SyntaxNode {
187 match self {
188 GeneratedFunctionTarget::BehindItem(it) => it,
189 GeneratedFunctionTarget::InEmptyItemList(it) => it,
190 }
191 }
192}
193
194fn fn_name(call: &ast::Path) -> Option<ast::Name> {
195 let name = call.segment()?.syntax().to_string();
196 Some(make::name(&name))
197}
198
199/// Computes the type variables and arguments required for the generated function
200fn fn_args(
201 ctx: &AssistContext,
202 target_module: hir::Module,
203 call: &ast::CallExpr,
204) -> Option<(Option<ast::GenericParamList>, ast::ParamList)> {
205 let mut arg_names = Vec::new();
206 let mut arg_types = Vec::new();
207 for arg in call.arg_list()?.args() {
208 arg_names.push(match fn_arg_name(&arg) {
209 Some(name) => name,
210 None => String::from("arg"),
211 });
212 arg_types.push(match fn_arg_type(ctx, target_module, &arg) {
213 Some(ty) => ty,
214 None => String::from("()"),
215 });
216 }
217 deduplicate_arg_names(&mut arg_names);
218 let params = arg_names
219 .into_iter()
220 .zip(arg_types)
221 .map(|(name, ty)| make::param(make::ident_pat(make::name(&name)).into(), make::ty(&ty)));
222 Some((None, make::param_list(None, params)))
223}
224
225/// Makes duplicate argument names unique by appending incrementing numbers.
226///
227/// ```
228/// let mut names: Vec<String> =
229/// vec!["foo".into(), "foo".into(), "bar".into(), "baz".into(), "bar".into()];
230/// deduplicate_arg_names(&mut names);
231/// let expected: Vec<String> =
232/// vec!["foo_1".into(), "foo_2".into(), "bar_1".into(), "baz".into(), "bar_2".into()];
233/// assert_eq!(names, expected);
234/// ```
235fn deduplicate_arg_names(arg_names: &mut Vec<String>) {
236 let arg_name_counts = arg_names.iter().fold(FxHashMap::default(), |mut m, name| {
237 *m.entry(name).or_insert(0) += 1;
238 m
239 });
240 let duplicate_arg_names: FxHashSet<String> = arg_name_counts
241 .into_iter()
242 .filter(|(_, count)| *count >= 2)
243 .map(|(name, _)| name.clone())
244 .collect();
245
246 let mut counter_per_name = FxHashMap::default();
247 for arg_name in arg_names.iter_mut() {
248 if duplicate_arg_names.contains(arg_name) {
249 let counter = counter_per_name.entry(arg_name.clone()).or_insert(1);
250 arg_name.push('_');
251 arg_name.push_str(&counter.to_string());
252 *counter += 1;
253 }
254 }
255}
256
257fn fn_arg_name(fn_arg: &ast::Expr) -> Option<String> {
258 match fn_arg {
259 ast::Expr::CastExpr(cast_expr) => fn_arg_name(&cast_expr.expr()?),
260 _ => Some(
261 fn_arg
262 .syntax()
263 .descendants()
264 .filter(|d| ast::NameRef::can_cast(d.kind()))
265 .last()?
266 .to_string(),
267 ),
268 }
269}
270
271fn fn_arg_type(
272 ctx: &AssistContext,
273 target_module: hir::Module,
274 fn_arg: &ast::Expr,
275) -> Option<String> {
276 let ty = ctx.sema.type_of_expr(fn_arg)?;
277 if ty.is_unknown() {
278 return None;
279 }
280
281 if let Ok(rendered) = ty.display_source_code(ctx.db(), target_module.into()) {
282 Some(rendered)
283 } else {
284 None
285 }
286}
287
288/// Returns the position inside the current mod or file
289/// directly after the current block
290/// We want to write the generated function directly after
291/// fns, impls or macro calls, but inside mods
292fn next_space_for_fn_after_call_site(expr: &ast::CallExpr) -> Option<GeneratedFunctionTarget> {
293 let mut ancestors = expr.syntax().ancestors().peekable();
294 let mut last_ancestor: Option<SyntaxNode> = None;
295 while let Some(next_ancestor) = ancestors.next() {
296 match next_ancestor.kind() {
297 SyntaxKind::SOURCE_FILE => {
298 break;
299 }
300 SyntaxKind::ITEM_LIST => {
301 if ancestors.peek().map(|a| a.kind()) == Some(SyntaxKind::MODULE) {
302 break;
303 }
304 }
305 _ => {}
306 }
307 last_ancestor = Some(next_ancestor);
308 }
309 last_ancestor.map(GeneratedFunctionTarget::BehindItem)
310}
311
312fn next_space_for_fn_in_module(
313 db: &dyn hir::db::AstDatabase,
314 module_source: &hir::InFile<hir::ModuleSource>,
315) -> Option<(FileId, GeneratedFunctionTarget)> {
316 let file = module_source.file_id.original_file(db);
317 let assist_item = match &module_source.value {
318 hir::ModuleSource::SourceFile(it) => {
319 if let Some(last_item) = it.items().last() {
320 GeneratedFunctionTarget::BehindItem(last_item.syntax().clone())
321 } else {
322 GeneratedFunctionTarget::BehindItem(it.syntax().clone())
323 }
324 }
325 hir::ModuleSource::Module(it) => {
326 if let Some(last_item) = it.item_list().and_then(|it| it.items().last()) {
327 GeneratedFunctionTarget::BehindItem(last_item.syntax().clone())
328 } else {
329 GeneratedFunctionTarget::InEmptyItemList(it.item_list()?.syntax().clone())
330 }
331 }
332 hir::ModuleSource::BlockExpr(it) => {
333 if let Some(last_item) =
334 it.statements().take_while(|stmt| matches!(stmt, ast::Stmt::Item(_))).last()
335 {
336 GeneratedFunctionTarget::BehindItem(last_item.syntax().clone())
337 } else {
338 GeneratedFunctionTarget::InEmptyItemList(it.syntax().clone())
339 }
340 }
341 };
342 Some((file, assist_item))
343}
344
345#[cfg(test)]
346mod tests {
347 use crate::tests::{check_assist, check_assist_not_applicable};
348
349 use super::*;
350
351 #[test]
352 fn add_function_with_no_args() {
353 check_assist(
354 generate_function,
355 r"
356fn foo() {
357 bar$0();
358}
359",
360 r"
361fn foo() {
362 bar();
363}
364
365fn bar() ${0:-> ()} {
366 todo!()
367}
368",
369 )
370 }
371
372 #[test]
373 fn add_function_from_method() {
374 // This ensures that the function is correctly generated
375 // in the next outer mod or file
376 check_assist(
377 generate_function,
378 r"
379impl Foo {
380 fn foo() {
381 bar$0();
382 }
383}
384",
385 r"
386impl Foo {
387 fn foo() {
388 bar();
389 }
390}
391
392fn bar() ${0:-> ()} {
393 todo!()
394}
395",
396 )
397 }
398
399 #[test]
400 fn add_function_directly_after_current_block() {
401 // The new fn should not be created at the end of the file or module
402 check_assist(
403 generate_function,
404 r"
405fn foo1() {
406 bar$0();
407}
408
409fn foo2() {}
410",
411 r"
412fn foo1() {
413 bar();
414}
415
416fn bar() ${0:-> ()} {
417 todo!()
418}
419
420fn foo2() {}
421",
422 )
423 }
424
425 #[test]
426 fn add_function_with_no_args_in_same_module() {
427 check_assist(
428 generate_function,
429 r"
430mod baz {
431 fn foo() {
432 bar$0();
433 }
434}
435",
436 r"
437mod baz {
438 fn foo() {
439 bar();
440 }
441
442 fn bar() ${0:-> ()} {
443 todo!()
444 }
445}
446",
447 )
448 }
449
450 #[test]
451 fn add_function_with_function_call_arg() {
452 check_assist(
453 generate_function,
454 r"
455struct Baz;
456fn baz() -> Baz { todo!() }
457fn foo() {
458 bar$0(baz());
459}
460",
461 r"
462struct Baz;
463fn baz() -> Baz { todo!() }
464fn foo() {
465 bar(baz());
466}
467
468fn bar(baz: Baz) ${0:-> ()} {
469 todo!()
470}
471",
472 );
473 }
474
475 #[test]
476 fn add_function_with_method_call_arg() {
477 check_assist(
478 generate_function,
479 r"
480struct Baz;
481impl Baz {
482 fn foo(&self) -> Baz {
483 ba$0r(self.baz())
484 }
485 fn baz(&self) -> Baz {
486 Baz
487 }
488}
489",
490 r"
491struct Baz;
492impl Baz {
493 fn foo(&self) -> Baz {
494 bar(self.baz())
495 }
496 fn baz(&self) -> Baz {
497 Baz
498 }
499}
500
501fn bar(baz: Baz) ${0:-> ()} {
502 todo!()
503}
504",
505 )
506 }
507
508 #[test]
509 fn add_function_with_string_literal_arg() {
510 check_assist(
511 generate_function,
512 r#"
513fn foo() {
514 $0bar("bar")
515}
516"#,
517 r#"
518fn foo() {
519 bar("bar")
520}
521
522fn bar(arg: &str) ${0:-> ()} {
523 todo!()
524}
525"#,
526 )
527 }
528
529 #[test]
530 fn add_function_with_char_literal_arg() {
531 check_assist(
532 generate_function,
533 r#"
534fn foo() {
535 $0bar('x')
536}
537"#,
538 r#"
539fn foo() {
540 bar('x')
541}
542
543fn bar(arg: char) ${0:-> ()} {
544 todo!()
545}
546"#,
547 )
548 }
549
550 #[test]
551 fn add_function_with_int_literal_arg() {
552 check_assist(
553 generate_function,
554 r"
555fn foo() {
556 $0bar(42)
557}
558",
559 r"
560fn foo() {
561 bar(42)
562}
563
564fn bar(arg: i32) ${0:-> ()} {
565 todo!()
566}
567",
568 )
569 }
570
571 #[test]
572 fn add_function_with_cast_int_literal_arg() {
573 check_assist(
574 generate_function,
575 r"
576fn foo() {
577 $0bar(42 as u8)
578}
579",
580 r"
581fn foo() {
582 bar(42 as u8)
583}
584
585fn bar(arg: u8) ${0:-> ()} {
586 todo!()
587}
588",
589 )
590 }
591
592 #[test]
593 fn name_of_cast_variable_is_used() {
594 // Ensures that the name of the cast type isn't used
595 // in the generated function signature.
596 check_assist(
597 generate_function,
598 r"
599fn foo() {
600 let x = 42;
601 bar$0(x as u8)
602}
603",
604 r"
605fn foo() {
606 let x = 42;
607 bar(x as u8)
608}
609
610fn bar(x: u8) ${0:-> ()} {
611 todo!()
612}
613",
614 )
615 }
616
617 #[test]
618 fn add_function_with_variable_arg() {
619 check_assist(
620 generate_function,
621 r"
622fn foo() {
623 let worble = ();
624 $0bar(worble)
625}
626",
627 r"
628fn foo() {
629 let worble = ();
630 bar(worble)
631}
632
633fn bar(worble: ()) ${0:-> ()} {
634 todo!()
635}
636",
637 )
638 }
639
640 #[test]
641 fn add_function_with_impl_trait_arg() {
642 check_assist(
643 generate_function,
644 r"
645trait Foo {}
646fn foo() -> impl Foo {
647 todo!()
648}
649fn baz() {
650 $0bar(foo())
651}
652",
653 r"
654trait Foo {}
655fn foo() -> impl Foo {
656 todo!()
657}
658fn baz() {
659 bar(foo())
660}
661
662fn bar(foo: impl Foo) ${0:-> ()} {
663 todo!()
664}
665",
666 )
667 }
668
669 #[test]
670 fn borrowed_arg() {
671 check_assist(
672 generate_function,
673 r"
674struct Baz;
675fn baz() -> Baz { todo!() }
676
677fn foo() {
678 bar$0(&baz())
679}
680",
681 r"
682struct Baz;
683fn baz() -> Baz { todo!() }
684
685fn foo() {
686 bar(&baz())
687}
688
689fn bar(baz: &Baz) ${0:-> ()} {
690 todo!()
691}
692",
693 )
694 }
695
696 #[test]
697 fn add_function_with_qualified_path_arg() {
698 check_assist(
699 generate_function,
700 r"
701mod Baz {
702 pub struct Bof;
703 pub fn baz() -> Bof { Bof }
704}
705fn foo() {
706 $0bar(Baz::baz())
707}
708",
709 r"
710mod Baz {
711 pub struct Bof;
712 pub fn baz() -> Bof { Bof }
713}
714fn foo() {
715 bar(Baz::baz())
716}
717
718fn bar(baz: Baz::Bof) ${0:-> ()} {
719 todo!()
720}
721",
722 )
723 }
724
725 #[test]
726 #[ignore]
727 // FIXME fix printing the generics of a `Ty` to make this test pass
728 fn add_function_with_generic_arg() {
729 check_assist(
730 generate_function,
731 r"
732fn foo<T>(t: T) {
733 $0bar(t)
734}
735",
736 r"
737fn foo<T>(t: T) {
738 bar(t)
739}
740
741fn bar<T>(t: T) ${0:-> ()} {
742 todo!()
743}
744",
745 )
746 }
747
748 #[test]
749 #[ignore]
750 // FIXME Fix function type printing to make this test pass
751 fn add_function_with_fn_arg() {
752 check_assist(
753 generate_function,
754 r"
755struct Baz;
756impl Baz {
757 fn new() -> Self { Baz }
758}
759fn foo() {
760 $0bar(Baz::new);
761}
762",
763 r"
764struct Baz;
765impl Baz {
766 fn new() -> Self { Baz }
767}
768fn foo() {
769 bar(Baz::new);
770}
771
772fn bar(arg: fn() -> Baz) ${0:-> ()} {
773 todo!()
774}
775",
776 )
777 }
778
779 #[test]
780 #[ignore]
781 // FIXME Fix closure type printing to make this test pass
782 fn add_function_with_closure_arg() {
783 check_assist(
784 generate_function,
785 r"
786fn foo() {
787 let closure = |x: i64| x - 1;
788 $0bar(closure)
789}
790",
791 r"
792fn foo() {
793 let closure = |x: i64| x - 1;
794 bar(closure)
795}
796
797fn bar(closure: impl Fn(i64) -> i64) ${0:-> ()} {
798 todo!()
799}
800",
801 )
802 }
803
804 #[test]
805 fn unresolveable_types_default_to_unit() {
806 check_assist(
807 generate_function,
808 r"
809fn foo() {
810 $0bar(baz)
811}
812",
813 r"
814fn foo() {
815 bar(baz)
816}
817
818fn bar(baz: ()) ${0:-> ()} {
819 todo!()
820}
821",
822 )
823 }
824
825 #[test]
826 fn arg_names_dont_overlap() {
827 check_assist(
828 generate_function,
829 r"
830struct Baz;
831fn baz() -> Baz { Baz }
832fn foo() {
833 $0bar(baz(), baz())
834}
835",
836 r"
837struct Baz;
838fn baz() -> Baz { Baz }
839fn foo() {
840 bar(baz(), baz())
841}
842
843fn bar(baz_1: Baz, baz_2: Baz) ${0:-> ()} {
844 todo!()
845}
846",
847 )
848 }
849
850 #[test]
851 fn arg_name_counters_start_at_1_per_name() {
852 check_assist(
853 generate_function,
854 r#"
855struct Baz;
856fn baz() -> Baz { Baz }
857fn foo() {
858 $0bar(baz(), baz(), "foo", "bar")
859}
860"#,
861 r#"
862struct Baz;
863fn baz() -> Baz { Baz }
864fn foo() {
865 bar(baz(), baz(), "foo", "bar")
866}
867
868fn bar(baz_1: Baz, baz_2: Baz, arg_1: &str, arg_2: &str) ${0:-> ()} {
869 todo!()
870}
871"#,
872 )
873 }
874
875 #[test]
876 fn add_function_in_module() {
877 check_assist(
878 generate_function,
879 r"
880mod bar {}
881
882fn foo() {
883 bar::my_fn$0()
884}
885",
886 r"
887mod bar {
888 pub(crate) fn my_fn() ${0:-> ()} {
889 todo!()
890 }
891}
892
893fn foo() {
894 bar::my_fn()
895}
896",
897 )
898 }
899
900 #[test]
901 #[ignore]
902 // Ignored until local imports are supported.
903 // See https://github.com/rust-analyzer/rust-analyzer/issues/1165
904 fn qualified_path_uses_correct_scope() {
905 check_assist(
906 generate_function,
907 "
908mod foo {
909 pub struct Foo;
910}
911fn bar() {
912 use foo::Foo;
913 let foo = Foo;
914 baz$0(foo)
915}
916",
917 "
918mod foo {
919 pub struct Foo;
920}
921fn bar() {
922 use foo::Foo;
923 let foo = Foo;
924 baz(foo)
925}
926
927fn baz(foo: foo::Foo) ${0:-> ()} {
928 todo!()
929}
930",
931 )
932 }
933
934 #[test]
935 fn add_function_in_module_containing_other_items() {
936 check_assist(
937 generate_function,
938 r"
939mod bar {
940 fn something_else() {}
941}
942
943fn foo() {
944 bar::my_fn$0()
945}
946",
947 r"
948mod bar {
949 fn something_else() {}
950
951 pub(crate) fn my_fn() ${0:-> ()} {
952 todo!()
953 }
954}
955
956fn foo() {
957 bar::my_fn()
958}
959",
960 )
961 }
962
963 #[test]
964 fn add_function_in_nested_module() {
965 check_assist(
966 generate_function,
967 r"
968mod bar {
969 mod baz {}
970}
971
972fn foo() {
973 bar::baz::my_fn$0()
974}
975",
976 r"
977mod bar {
978 mod baz {
979 pub(crate) fn my_fn() ${0:-> ()} {
980 todo!()
981 }
982 }
983}
984
985fn foo() {
986 bar::baz::my_fn()
987}
988",
989 )
990 }
991
992 #[test]
993 fn add_function_in_another_file() {
994 check_assist(
995 generate_function,
996 r"
997//- /main.rs
998mod foo;
999
1000fn main() {
1001 foo::bar$0()
1002}
1003//- /foo.rs
1004",
1005 r"
1006
1007
1008pub(crate) fn bar() ${0:-> ()} {
1009 todo!()
1010}",
1011 )
1012 }
1013
1014 #[test]
1015 fn add_function_not_applicable_if_function_already_exists() {
1016 check_assist_not_applicable(
1017 generate_function,
1018 r"
1019fn foo() {
1020 bar$0();
1021}
1022
1023fn bar() {}
1024",
1025 )
1026 }
1027
1028 #[test]
1029 fn add_function_not_applicable_if_unresolved_variable_in_call_is_selected() {
1030 check_assist_not_applicable(
1031 // bar is resolved, but baz isn't.
1032 // The assist is only active if the cursor is on an unresolved path,
1033 // but the assist should only be offered if the path is a function call.
1034 generate_function,
1035 r"
1036fn foo() {
1037 bar(b$0az);
1038}
1039
1040fn bar(baz: ()) {}
1041",
1042 )
1043 }
1044
1045 #[test]
1046 #[ignore]
1047 fn create_method_with_no_args() {
1048 check_assist(
1049 generate_function,
1050 r"
1051struct Foo;
1052impl Foo {
1053 fn foo(&self) {
1054 self.bar()$0;
1055 }
1056}
1057 ",
1058 r"
1059struct Foo;
1060impl Foo {
1061 fn foo(&self) {
1062 self.bar();
1063 }
1064 fn bar(&self) {
1065 todo!();
1066 }
1067}
1068 ",
1069 )
1070 }
1071}