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