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