]> git.lizzy.rs Git - rust.git/blob - crates/assists/src/handlers/generate_function.rs
Update completions test output
[rust.git] / crates / assists / src / handlers / generate_function.rs
1 use hir::HirDisplay;
2 use ide_db::{base_db::FileId, helpers::SnippetCap};
3 use rustc_hash::{FxHashMap, FxHashSet};
4 use syntax::{
5     ast::{
6         self,
7         edit::{AstNodeEdit, IndentLevel},
8         make, ArgListOwner, AstNode, ModuleItemOwner,
9     },
10     SyntaxKind, SyntaxNode, TextSize,
11 };
12
13 use 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 // ```
43 pub(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
80 struct 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
89 impl 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
101 struct 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
110 impl 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.syntax());
162                 leading_ws = format!("\n{}", indent + 1);
163                 fn_def = fn_def.indent(indent + 1);
164                 trailing_ws = format!("\n{}", indent);
165                 it.syntax().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
180 enum GeneratedFunctionTarget {
181     BehindItem(SyntaxNode),
182     InEmptyItemList(ast::ItemList),
183 }
184
185 impl GeneratedFunctionTarget {
186     fn syntax(&self) -> &SyntaxNode {
187         match self {
188             GeneratedFunctionTarget::BehindItem(it) => it,
189             GeneratedFunctionTarget::InEmptyItemList(it) => it.syntax(),
190         }
191     }
192 }
193
194 fn 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
200 fn 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.into_iter().zip(arg_types).map(|(name, ty)| make::param(name, ty));
219     Some((None, make::param_list(params)))
220 }
221
222 /// Makes duplicate argument names unique by appending incrementing numbers.
223 ///
224 /// ```
225 /// let mut names: Vec<String> =
226 ///     vec!["foo".into(), "foo".into(), "bar".into(), "baz".into(), "bar".into()];
227 /// deduplicate_arg_names(&mut names);
228 /// let expected: Vec<String> =
229 ///     vec!["foo_1".into(), "foo_2".into(), "bar_1".into(), "baz".into(), "bar_2".into()];
230 /// assert_eq!(names, expected);
231 /// ```
232 fn deduplicate_arg_names(arg_names: &mut Vec<String>) {
233     let arg_name_counts = arg_names.iter().fold(FxHashMap::default(), |mut m, name| {
234         *m.entry(name).or_insert(0) += 1;
235         m
236     });
237     let duplicate_arg_names: FxHashSet<String> = arg_name_counts
238         .into_iter()
239         .filter(|(_, count)| *count >= 2)
240         .map(|(name, _)| name.clone())
241         .collect();
242
243     let mut counter_per_name = FxHashMap::default();
244     for arg_name in arg_names.iter_mut() {
245         if duplicate_arg_names.contains(arg_name) {
246             let counter = counter_per_name.entry(arg_name.clone()).or_insert(1);
247             arg_name.push('_');
248             arg_name.push_str(&counter.to_string());
249             *counter += 1;
250         }
251     }
252 }
253
254 fn fn_arg_name(fn_arg: &ast::Expr) -> Option<String> {
255     match fn_arg {
256         ast::Expr::CastExpr(cast_expr) => fn_arg_name(&cast_expr.expr()?),
257         _ => Some(
258             fn_arg
259                 .syntax()
260                 .descendants()
261                 .filter(|d| ast::NameRef::can_cast(d.kind()))
262                 .last()?
263                 .to_string(),
264         ),
265     }
266 }
267
268 fn fn_arg_type(
269     ctx: &AssistContext,
270     target_module: hir::Module,
271     fn_arg: &ast::Expr,
272 ) -> Option<String> {
273     let ty = ctx.sema.type_of_expr(fn_arg)?;
274     if ty.is_unknown() {
275         return None;
276     }
277
278     if let Ok(rendered) = ty.display_source_code(ctx.db(), target_module.into()) {
279         Some(rendered)
280     } else {
281         None
282     }
283 }
284
285 /// Returns the position inside the current mod or file
286 /// directly after the current block
287 /// We want to write the generated function directly after
288 /// fns, impls or macro calls, but inside mods
289 fn next_space_for_fn_after_call_site(expr: &ast::CallExpr) -> Option<GeneratedFunctionTarget> {
290     let mut ancestors = expr.syntax().ancestors().peekable();
291     let mut last_ancestor: Option<SyntaxNode> = None;
292     while let Some(next_ancestor) = ancestors.next() {
293         match next_ancestor.kind() {
294             SyntaxKind::SOURCE_FILE => {
295                 break;
296             }
297             SyntaxKind::ITEM_LIST => {
298                 if ancestors.peek().map(|a| a.kind()) == Some(SyntaxKind::MODULE) {
299                     break;
300                 }
301             }
302             _ => {}
303         }
304         last_ancestor = Some(next_ancestor);
305     }
306     last_ancestor.map(GeneratedFunctionTarget::BehindItem)
307 }
308
309 fn next_space_for_fn_in_module(
310     db: &dyn hir::db::AstDatabase,
311     module_source: &hir::InFile<hir::ModuleSource>,
312 ) -> Option<(FileId, GeneratedFunctionTarget)> {
313     let file = module_source.file_id.original_file(db);
314     let assist_item = match &module_source.value {
315         hir::ModuleSource::SourceFile(it) => {
316             if let Some(last_item) = it.items().last() {
317                 GeneratedFunctionTarget::BehindItem(last_item.syntax().clone())
318             } else {
319                 GeneratedFunctionTarget::BehindItem(it.syntax().clone())
320             }
321         }
322         hir::ModuleSource::Module(it) => {
323             if let Some(last_item) = it.item_list().and_then(|it| it.items().last()) {
324                 GeneratedFunctionTarget::BehindItem(last_item.syntax().clone())
325             } else {
326                 GeneratedFunctionTarget::InEmptyItemList(it.item_list()?)
327             }
328         }
329     };
330     Some((file, assist_item))
331 }
332
333 #[cfg(test)]
334 mod tests {
335     use crate::tests::{check_assist, check_assist_not_applicable};
336
337     use super::*;
338
339     #[test]
340     fn add_function_with_no_args() {
341         check_assist(
342             generate_function,
343             r"
344 fn foo() {
345     bar$0();
346 }
347 ",
348             r"
349 fn foo() {
350     bar();
351 }
352
353 fn bar() ${0:-> ()} {
354     todo!()
355 }
356 ",
357         )
358     }
359
360     #[test]
361     fn add_function_from_method() {
362         // This ensures that the function is correctly generated
363         // in the next outer mod or file
364         check_assist(
365             generate_function,
366             r"
367 impl Foo {
368     fn foo() {
369         bar$0();
370     }
371 }
372 ",
373             r"
374 impl Foo {
375     fn foo() {
376         bar();
377     }
378 }
379
380 fn bar() ${0:-> ()} {
381     todo!()
382 }
383 ",
384         )
385     }
386
387     #[test]
388     fn add_function_directly_after_current_block() {
389         // The new fn should not be created at the end of the file or module
390         check_assist(
391             generate_function,
392             r"
393 fn foo1() {
394     bar$0();
395 }
396
397 fn foo2() {}
398 ",
399             r"
400 fn foo1() {
401     bar();
402 }
403
404 fn bar() ${0:-> ()} {
405     todo!()
406 }
407
408 fn foo2() {}
409 ",
410         )
411     }
412
413     #[test]
414     fn add_function_with_no_args_in_same_module() {
415         check_assist(
416             generate_function,
417             r"
418 mod baz {
419     fn foo() {
420         bar$0();
421     }
422 }
423 ",
424             r"
425 mod baz {
426     fn foo() {
427         bar();
428     }
429
430     fn bar() ${0:-> ()} {
431         todo!()
432     }
433 }
434 ",
435         )
436     }
437
438     #[test]
439     fn add_function_with_function_call_arg() {
440         check_assist(
441             generate_function,
442             r"
443 struct Baz;
444 fn baz() -> Baz { todo!() }
445 fn foo() {
446     bar$0(baz());
447 }
448 ",
449             r"
450 struct Baz;
451 fn baz() -> Baz { todo!() }
452 fn foo() {
453     bar(baz());
454 }
455
456 fn bar(baz: Baz) ${0:-> ()} {
457     todo!()
458 }
459 ",
460         );
461     }
462
463     #[test]
464     fn add_function_with_method_call_arg() {
465         check_assist(
466             generate_function,
467             r"
468 struct Baz;
469 impl Baz {
470     fn foo(&self) -> Baz {
471         ba$0r(self.baz())
472     }
473     fn baz(&self) -> Baz {
474         Baz
475     }
476 }
477 ",
478             r"
479 struct Baz;
480 impl Baz {
481     fn foo(&self) -> Baz {
482         bar(self.baz())
483     }
484     fn baz(&self) -> Baz {
485         Baz
486     }
487 }
488
489 fn bar(baz: Baz) ${0:-> ()} {
490     todo!()
491 }
492 ",
493         )
494     }
495
496     #[test]
497     fn add_function_with_string_literal_arg() {
498         check_assist(
499             generate_function,
500             r#"
501 fn foo() {
502     $0bar("bar")
503 }
504 "#,
505             r#"
506 fn foo() {
507     bar("bar")
508 }
509
510 fn bar(arg: &str) ${0:-> ()} {
511     todo!()
512 }
513 "#,
514         )
515     }
516
517     #[test]
518     fn add_function_with_char_literal_arg() {
519         check_assist(
520             generate_function,
521             r#"
522 fn foo() {
523     $0bar('x')
524 }
525 "#,
526             r#"
527 fn foo() {
528     bar('x')
529 }
530
531 fn bar(arg: char) ${0:-> ()} {
532     todo!()
533 }
534 "#,
535         )
536     }
537
538     #[test]
539     fn add_function_with_int_literal_arg() {
540         check_assist(
541             generate_function,
542             r"
543 fn foo() {
544     $0bar(42)
545 }
546 ",
547             r"
548 fn foo() {
549     bar(42)
550 }
551
552 fn bar(arg: i32) ${0:-> ()} {
553     todo!()
554 }
555 ",
556         )
557     }
558
559     #[test]
560     fn add_function_with_cast_int_literal_arg() {
561         check_assist(
562             generate_function,
563             r"
564 fn foo() {
565     $0bar(42 as u8)
566 }
567 ",
568             r"
569 fn foo() {
570     bar(42 as u8)
571 }
572
573 fn bar(arg: u8) ${0:-> ()} {
574     todo!()
575 }
576 ",
577         )
578     }
579
580     #[test]
581     fn name_of_cast_variable_is_used() {
582         // Ensures that the name of the cast type isn't used
583         // in the generated function signature.
584         check_assist(
585             generate_function,
586             r"
587 fn foo() {
588     let x = 42;
589     bar$0(x as u8)
590 }
591 ",
592             r"
593 fn foo() {
594     let x = 42;
595     bar(x as u8)
596 }
597
598 fn bar(x: u8) ${0:-> ()} {
599     todo!()
600 }
601 ",
602         )
603     }
604
605     #[test]
606     fn add_function_with_variable_arg() {
607         check_assist(
608             generate_function,
609             r"
610 fn foo() {
611     let worble = ();
612     $0bar(worble)
613 }
614 ",
615             r"
616 fn foo() {
617     let worble = ();
618     bar(worble)
619 }
620
621 fn bar(worble: ()) ${0:-> ()} {
622     todo!()
623 }
624 ",
625         )
626     }
627
628     #[test]
629     fn add_function_with_impl_trait_arg() {
630         check_assist(
631             generate_function,
632             r"
633 trait Foo {}
634 fn foo() -> impl Foo {
635     todo!()
636 }
637 fn baz() {
638     $0bar(foo())
639 }
640 ",
641             r"
642 trait Foo {}
643 fn foo() -> impl Foo {
644     todo!()
645 }
646 fn baz() {
647     bar(foo())
648 }
649
650 fn bar(foo: impl Foo) ${0:-> ()} {
651     todo!()
652 }
653 ",
654         )
655     }
656
657     #[test]
658     fn borrowed_arg() {
659         check_assist(
660             generate_function,
661             r"
662 struct Baz;
663 fn baz() -> Baz { todo!() }
664
665 fn foo() {
666     bar$0(&baz())
667 }
668 ",
669             r"
670 struct Baz;
671 fn baz() -> Baz { todo!() }
672
673 fn foo() {
674     bar(&baz())
675 }
676
677 fn bar(baz: &Baz) ${0:-> ()} {
678     todo!()
679 }
680 ",
681         )
682     }
683
684     #[test]
685     fn add_function_with_qualified_path_arg() {
686         check_assist(
687             generate_function,
688             r"
689 mod Baz {
690     pub struct Bof;
691     pub fn baz() -> Bof { Bof }
692 }
693 fn foo() {
694     $0bar(Baz::baz())
695 }
696 ",
697             r"
698 mod Baz {
699     pub struct Bof;
700     pub fn baz() -> Bof { Bof }
701 }
702 fn foo() {
703     bar(Baz::baz())
704 }
705
706 fn bar(baz: Baz::Bof) ${0:-> ()} {
707     todo!()
708 }
709 ",
710         )
711     }
712
713     #[test]
714     #[ignore]
715     // FIXME fix printing the generics of a `Ty` to make this test pass
716     fn add_function_with_generic_arg() {
717         check_assist(
718             generate_function,
719             r"
720 fn foo<T>(t: T) {
721     $0bar(t)
722 }
723 ",
724             r"
725 fn foo<T>(t: T) {
726     bar(t)
727 }
728
729 fn bar<T>(t: T) ${0:-> ()} {
730     todo!()
731 }
732 ",
733         )
734     }
735
736     #[test]
737     #[ignore]
738     // FIXME Fix function type printing to make this test pass
739     fn add_function_with_fn_arg() {
740         check_assist(
741             generate_function,
742             r"
743 struct Baz;
744 impl Baz {
745     fn new() -> Self { Baz }
746 }
747 fn foo() {
748     $0bar(Baz::new);
749 }
750 ",
751             r"
752 struct Baz;
753 impl Baz {
754     fn new() -> Self { Baz }
755 }
756 fn foo() {
757     bar(Baz::new);
758 }
759
760 fn bar(arg: fn() -> Baz) ${0:-> ()} {
761     todo!()
762 }
763 ",
764         )
765     }
766
767     #[test]
768     #[ignore]
769     // FIXME Fix closure type printing to make this test pass
770     fn add_function_with_closure_arg() {
771         check_assist(
772             generate_function,
773             r"
774 fn foo() {
775     let closure = |x: i64| x - 1;
776     $0bar(closure)
777 }
778 ",
779             r"
780 fn foo() {
781     let closure = |x: i64| x - 1;
782     bar(closure)
783 }
784
785 fn bar(closure: impl Fn(i64) -> i64) ${0:-> ()} {
786     todo!()
787 }
788 ",
789         )
790     }
791
792     #[test]
793     fn unresolveable_types_default_to_unit() {
794         check_assist(
795             generate_function,
796             r"
797 fn foo() {
798     $0bar(baz)
799 }
800 ",
801             r"
802 fn foo() {
803     bar(baz)
804 }
805
806 fn bar(baz: ()) ${0:-> ()} {
807     todo!()
808 }
809 ",
810         )
811     }
812
813     #[test]
814     fn arg_names_dont_overlap() {
815         check_assist(
816             generate_function,
817             r"
818 struct Baz;
819 fn baz() -> Baz { Baz }
820 fn foo() {
821     $0bar(baz(), baz())
822 }
823 ",
824             r"
825 struct Baz;
826 fn baz() -> Baz { Baz }
827 fn foo() {
828     bar(baz(), baz())
829 }
830
831 fn bar(baz_1: Baz, baz_2: Baz) ${0:-> ()} {
832     todo!()
833 }
834 ",
835         )
836     }
837
838     #[test]
839     fn arg_name_counters_start_at_1_per_name() {
840         check_assist(
841             generate_function,
842             r#"
843 struct Baz;
844 fn baz() -> Baz { Baz }
845 fn foo() {
846     $0bar(baz(), baz(), "foo", "bar")
847 }
848 "#,
849             r#"
850 struct Baz;
851 fn baz() -> Baz { Baz }
852 fn foo() {
853     bar(baz(), baz(), "foo", "bar")
854 }
855
856 fn bar(baz_1: Baz, baz_2: Baz, arg_1: &str, arg_2: &str) ${0:-> ()} {
857     todo!()
858 }
859 "#,
860         )
861     }
862
863     #[test]
864     fn add_function_in_module() {
865         check_assist(
866             generate_function,
867             r"
868 mod bar {}
869
870 fn foo() {
871     bar::my_fn$0()
872 }
873 ",
874             r"
875 mod bar {
876     pub(crate) fn my_fn() ${0:-> ()} {
877         todo!()
878     }
879 }
880
881 fn foo() {
882     bar::my_fn()
883 }
884 ",
885         )
886     }
887
888     #[test]
889     #[ignore]
890     // Ignored until local imports are supported.
891     // See https://github.com/rust-analyzer/rust-analyzer/issues/1165
892     fn qualified_path_uses_correct_scope() {
893         check_assist(
894             generate_function,
895             "
896 mod foo {
897     pub struct Foo;
898 }
899 fn bar() {
900     use foo::Foo;
901     let foo = Foo;
902     baz$0(foo)
903 }
904 ",
905             "
906 mod foo {
907     pub struct Foo;
908 }
909 fn bar() {
910     use foo::Foo;
911     let foo = Foo;
912     baz(foo)
913 }
914
915 fn baz(foo: foo::Foo) ${0:-> ()} {
916     todo!()
917 }
918 ",
919         )
920     }
921
922     #[test]
923     fn add_function_in_module_containing_other_items() {
924         check_assist(
925             generate_function,
926             r"
927 mod bar {
928     fn something_else() {}
929 }
930
931 fn foo() {
932     bar::my_fn$0()
933 }
934 ",
935             r"
936 mod bar {
937     fn something_else() {}
938
939     pub(crate) fn my_fn() ${0:-> ()} {
940         todo!()
941     }
942 }
943
944 fn foo() {
945     bar::my_fn()
946 }
947 ",
948         )
949     }
950
951     #[test]
952     fn add_function_in_nested_module() {
953         check_assist(
954             generate_function,
955             r"
956 mod bar {
957     mod baz {}
958 }
959
960 fn foo() {
961     bar::baz::my_fn$0()
962 }
963 ",
964             r"
965 mod bar {
966     mod baz {
967         pub(crate) fn my_fn() ${0:-> ()} {
968             todo!()
969         }
970     }
971 }
972
973 fn foo() {
974     bar::baz::my_fn()
975 }
976 ",
977         )
978     }
979
980     #[test]
981     fn add_function_in_another_file() {
982         check_assist(
983             generate_function,
984             r"
985 //- /main.rs
986 mod foo;
987
988 fn main() {
989     foo::bar$0()
990 }
991 //- /foo.rs
992 ",
993             r"
994
995
996 pub(crate) fn bar() ${0:-> ()} {
997     todo!()
998 }",
999         )
1000     }
1001
1002     #[test]
1003     fn add_function_not_applicable_if_function_already_exists() {
1004         check_assist_not_applicable(
1005             generate_function,
1006             r"
1007 fn foo() {
1008     bar$0();
1009 }
1010
1011 fn bar() {}
1012 ",
1013         )
1014     }
1015
1016     #[test]
1017     fn add_function_not_applicable_if_unresolved_variable_in_call_is_selected() {
1018         check_assist_not_applicable(
1019             // bar is resolved, but baz isn't.
1020             // The assist is only active if the cursor is on an unresolved path,
1021             // but the assist should only be offered if the path is a function call.
1022             generate_function,
1023             r"
1024 fn foo() {
1025     bar(b$0az);
1026 }
1027
1028 fn bar(baz: ()) {}
1029 ",
1030         )
1031     }
1032
1033     #[test]
1034     #[ignore]
1035     fn create_method_with_no_args() {
1036         check_assist(
1037             generate_function,
1038             r"
1039 struct Foo;
1040 impl Foo {
1041     fn foo(&self) {
1042         self.bar()$0;
1043     }
1044 }
1045         ",
1046             r"
1047 struct Foo;
1048 impl Foo {
1049     fn foo(&self) {
1050         self.bar();
1051     }
1052     fn bar(&self) {
1053         todo!();
1054     }
1055 }
1056         ",
1057         )
1058     }
1059 }