diff options
Diffstat (limited to 'crates/assists/src/handlers/generate_function.rs')
-rw-r--r-- | crates/assists/src/handlers/generate_function.rs | 1059 |
1 files changed, 0 insertions, 1059 deletions
diff --git a/crates/assists/src/handlers/generate_function.rs b/crates/assists/src/handlers/generate_function.rs deleted file mode 100644 index 06ac85f67..000000000 --- a/crates/assists/src/handlers/generate_function.rs +++ /dev/null | |||
@@ -1,1059 +0,0 @@ | |||
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 | } | ||