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