diff options
Diffstat (limited to 'crates/ra_assists/src/handlers/generate_function.rs')
-rw-r--r-- | crates/ra_assists/src/handlers/generate_function.rs | 1058 |
1 files changed, 1058 insertions, 0 deletions
diff --git a/crates/ra_assists/src/handlers/generate_function.rs b/crates/ra_assists/src/handlers/generate_function.rs new file mode 100644 index 000000000..56510861d --- /dev/null +++ b/crates/ra_assists/src/handlers/generate_function.rs | |||
@@ -0,0 +1,1058 @@ | |||
1 | use hir::HirDisplay; | ||
2 | use ra_db::FileId; | ||
3 | use ra_syntax::{ | ||
4 | ast::{ | ||
5 | self, | ||
6 | edit::{AstNodeEdit, IndentLevel}, | ||
7 | make, ArgListOwner, AstNode, ModuleItemOwner, | ||
8 | }, | ||
9 | SyntaxKind, SyntaxNode, TextSize, | ||
10 | }; | ||
11 | use rustc_hash::{FxHashMap, FxHashSet}; | ||
12 | |||
13 | use 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 | // ``` | ||
44 | pub(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 | |||
81 | struct 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 | |||
90 | impl 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 | |||
104 | struct 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 | |||
113 | impl 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_def(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 | |||
179 | enum GeneratedFunctionTarget { | ||
180 | BehindItem(SyntaxNode), | ||
181 | InEmptyItemList(ast::ItemList), | ||
182 | } | ||
183 | |||
184 | impl GeneratedFunctionTarget { | ||
185 | fn syntax(&self) -> &SyntaxNode { | ||
186 | match self { | ||
187 | GeneratedFunctionTarget::BehindItem(it) => it, | ||
188 | GeneratedFunctionTarget::InEmptyItemList(it) => it.syntax(), | ||
189 | } | ||
190 | } | ||
191 | } | ||
192 | |||
193 | fn 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 | ||
199 | fn 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 | /// ``` | ||
231 | fn 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 | |||
253 | fn 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 | |||
267 | fn 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 | ||
288 | fn 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 | |||
308 | fn 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)] | ||
333 | mod 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" | ||
343 | fn foo() { | ||
344 | bar<|>(); | ||
345 | } | ||
346 | ", | ||
347 | r" | ||
348 | fn foo() { | ||
349 | bar(); | ||
350 | } | ||
351 | |||
352 | fn 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" | ||
366 | impl Foo { | ||
367 | fn foo() { | ||
368 | bar<|>(); | ||
369 | } | ||
370 | } | ||
371 | ", | ||
372 | r" | ||
373 | impl Foo { | ||
374 | fn foo() { | ||
375 | bar(); | ||
376 | } | ||
377 | } | ||
378 | |||
379 | fn 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" | ||
392 | fn foo1() { | ||
393 | bar<|>(); | ||
394 | } | ||
395 | |||
396 | fn foo2() {} | ||
397 | ", | ||
398 | r" | ||
399 | fn foo1() { | ||
400 | bar(); | ||
401 | } | ||
402 | |||
403 | fn bar() { | ||
404 | ${0:todo!()} | ||
405 | } | ||
406 | |||
407 | fn 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" | ||
417 | mod baz { | ||
418 | fn foo() { | ||
419 | bar<|>(); | ||
420 | } | ||
421 | } | ||
422 | ", | ||
423 | r" | ||
424 | mod 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" | ||
442 | struct Baz; | ||
443 | fn baz() -> Baz { todo!() } | ||
444 | fn foo() { | ||
445 | bar<|>(baz()); | ||
446 | } | ||
447 | ", | ||
448 | r" | ||
449 | struct Baz; | ||
450 | fn baz() -> Baz { todo!() } | ||
451 | fn foo() { | ||
452 | bar(baz()); | ||
453 | } | ||
454 | |||
455 | fn 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" | ||
467 | struct Baz; | ||
468 | impl Baz { | ||
469 | fn foo(&self) -> Baz { | ||
470 | ba<|>r(self.baz()) | ||
471 | } | ||
472 | fn baz(&self) -> Baz { | ||
473 | Baz | ||
474 | } | ||
475 | } | ||
476 | ", | ||
477 | r" | ||
478 | struct Baz; | ||
479 | impl Baz { | ||
480 | fn foo(&self) -> Baz { | ||
481 | bar(self.baz()) | ||
482 | } | ||
483 | fn baz(&self) -> Baz { | ||
484 | Baz | ||
485 | } | ||
486 | } | ||
487 | |||
488 | fn 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#" | ||
500 | fn foo() { | ||
501 | <|>bar("bar") | ||
502 | } | ||
503 | "#, | ||
504 | r#" | ||
505 | fn foo() { | ||
506 | bar("bar") | ||
507 | } | ||
508 | |||
509 | fn 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#" | ||
521 | fn foo() { | ||
522 | <|>bar('x') | ||
523 | } | ||
524 | "#, | ||
525 | r#" | ||
526 | fn foo() { | ||
527 | bar('x') | ||
528 | } | ||
529 | |||
530 | fn 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" | ||
542 | fn foo() { | ||
543 | <|>bar(42) | ||
544 | } | ||
545 | ", | ||
546 | r" | ||
547 | fn foo() { | ||
548 | bar(42) | ||
549 | } | ||
550 | |||
551 | fn 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" | ||
563 | fn foo() { | ||
564 | <|>bar(42 as u8) | ||
565 | } | ||
566 | ", | ||
567 | r" | ||
568 | fn foo() { | ||
569 | bar(42 as u8) | ||
570 | } | ||
571 | |||
572 | fn 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" | ||
586 | fn foo() { | ||
587 | let x = 42; | ||
588 | bar<|>(x as u8) | ||
589 | } | ||
590 | ", | ||
591 | r" | ||
592 | fn foo() { | ||
593 | let x = 42; | ||
594 | bar(x as u8) | ||
595 | } | ||
596 | |||
597 | fn 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" | ||
609 | fn foo() { | ||
610 | let worble = (); | ||
611 | <|>bar(worble) | ||
612 | } | ||
613 | ", | ||
614 | r" | ||
615 | fn foo() { | ||
616 | let worble = (); | ||
617 | bar(worble) | ||
618 | } | ||
619 | |||
620 | fn 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" | ||
632 | trait Foo {} | ||
633 | fn foo() -> impl Foo { | ||
634 | todo!() | ||
635 | } | ||
636 | fn baz() { | ||
637 | <|>bar(foo()) | ||
638 | } | ||
639 | ", | ||
640 | r" | ||
641 | trait Foo {} | ||
642 | fn foo() -> impl Foo { | ||
643 | todo!() | ||
644 | } | ||
645 | fn baz() { | ||
646 | bar(foo()) | ||
647 | } | ||
648 | |||
649 | fn 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" | ||
661 | struct Baz; | ||
662 | fn baz() -> Baz { todo!() } | ||
663 | |||
664 | fn foo() { | ||
665 | bar<|>(&baz()) | ||
666 | } | ||
667 | ", | ||
668 | r" | ||
669 | struct Baz; | ||
670 | fn baz() -> Baz { todo!() } | ||
671 | |||
672 | fn foo() { | ||
673 | bar(&baz()) | ||
674 | } | ||
675 | |||
676 | fn 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" | ||
688 | mod Baz { | ||
689 | pub struct Bof; | ||
690 | pub fn baz() -> Bof { Bof } | ||
691 | } | ||
692 | fn foo() { | ||
693 | <|>bar(Baz::baz()) | ||
694 | } | ||
695 | ", | ||
696 | r" | ||
697 | mod Baz { | ||
698 | pub struct Bof; | ||
699 | pub fn baz() -> Bof { Bof } | ||
700 | } | ||
701 | fn foo() { | ||
702 | bar(Baz::baz()) | ||
703 | } | ||
704 | |||
705 | fn 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" | ||
719 | fn foo<T>(t: T) { | ||
720 | <|>bar(t) | ||
721 | } | ||
722 | ", | ||
723 | r" | ||
724 | fn foo<T>(t: T) { | ||
725 | bar(t) | ||
726 | } | ||
727 | |||
728 | fn 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" | ||
742 | struct Baz; | ||
743 | impl Baz { | ||
744 | fn new() -> Self { Baz } | ||
745 | } | ||
746 | fn foo() { | ||
747 | <|>bar(Baz::new); | ||
748 | } | ||
749 | ", | ||
750 | r" | ||
751 | struct Baz; | ||
752 | impl Baz { | ||
753 | fn new() -> Self { Baz } | ||
754 | } | ||
755 | fn foo() { | ||
756 | bar(Baz::new); | ||
757 | } | ||
758 | |||
759 | fn 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" | ||
773 | fn foo() { | ||
774 | let closure = |x: i64| x - 1; | ||
775 | <|>bar(closure) | ||
776 | } | ||
777 | ", | ||
778 | r" | ||
779 | fn foo() { | ||
780 | let closure = |x: i64| x - 1; | ||
781 | bar(closure) | ||
782 | } | ||
783 | |||
784 | fn 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" | ||
796 | fn foo() { | ||
797 | <|>bar(baz) | ||
798 | } | ||
799 | ", | ||
800 | r" | ||
801 | fn foo() { | ||
802 | bar(baz) | ||
803 | } | ||
804 | |||
805 | fn 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" | ||
817 | struct Baz; | ||
818 | fn baz() -> Baz { Baz } | ||
819 | fn foo() { | ||
820 | <|>bar(baz(), baz()) | ||
821 | } | ||
822 | ", | ||
823 | r" | ||
824 | struct Baz; | ||
825 | fn baz() -> Baz { Baz } | ||
826 | fn foo() { | ||
827 | bar(baz(), baz()) | ||
828 | } | ||
829 | |||
830 | fn 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#" | ||
842 | struct Baz; | ||
843 | fn baz() -> Baz { Baz } | ||
844 | fn foo() { | ||
845 | <|>bar(baz(), baz(), "foo", "bar") | ||
846 | } | ||
847 | "#, | ||
848 | r#" | ||
849 | struct Baz; | ||
850 | fn baz() -> Baz { Baz } | ||
851 | fn foo() { | ||
852 | bar(baz(), baz(), "foo", "bar") | ||
853 | } | ||
854 | |||
855 | fn 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" | ||
867 | mod bar {} | ||
868 | |||
869 | fn foo() { | ||
870 | bar::my_fn<|>() | ||
871 | } | ||
872 | ", | ||
873 | r" | ||
874 | mod bar { | ||
875 | pub(crate) fn my_fn() { | ||
876 | ${0:todo!()} | ||
877 | } | ||
878 | } | ||
879 | |||
880 | fn 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 | " | ||
895 | mod foo { | ||
896 | pub struct Foo; | ||
897 | } | ||
898 | fn bar() { | ||
899 | use foo::Foo; | ||
900 | let foo = Foo; | ||
901 | baz<|>(foo) | ||
902 | } | ||
903 | ", | ||
904 | " | ||
905 | mod foo { | ||
906 | pub struct Foo; | ||
907 | } | ||
908 | fn bar() { | ||
909 | use foo::Foo; | ||
910 | let foo = Foo; | ||
911 | baz(foo) | ||
912 | } | ||
913 | |||
914 | fn 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" | ||
926 | mod bar { | ||
927 | fn something_else() {} | ||
928 | } | ||
929 | |||
930 | fn foo() { | ||
931 | bar::my_fn<|>() | ||
932 | } | ||
933 | ", | ||
934 | r" | ||
935 | mod bar { | ||
936 | fn something_else() {} | ||
937 | |||
938 | pub(crate) fn my_fn() { | ||
939 | ${0:todo!()} | ||
940 | } | ||
941 | } | ||
942 | |||
943 | fn 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" | ||
955 | mod bar { | ||
956 | mod baz {} | ||
957 | } | ||
958 | |||
959 | fn foo() { | ||
960 | bar::baz::my_fn<|>() | ||
961 | } | ||
962 | ", | ||
963 | r" | ||
964 | mod bar { | ||
965 | mod baz { | ||
966 | pub(crate) fn my_fn() { | ||
967 | ${0:todo!()} | ||
968 | } | ||
969 | } | ||
970 | } | ||
971 | |||
972 | fn 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 | ||
985 | mod foo; | ||
986 | |||
987 | fn main() { | ||
988 | foo::bar<|>() | ||
989 | } | ||
990 | //- /foo.rs | ||
991 | ", | ||
992 | r" | ||
993 | |||
994 | |||
995 | pub(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" | ||
1006 | fn foo() { | ||
1007 | bar<|>(); | ||
1008 | } | ||
1009 | |||
1010 | fn 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" | ||
1023 | fn foo() { | ||
1024 | bar(b<|>az); | ||
1025 | } | ||
1026 | |||
1027 | fn 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" | ||
1038 | struct Foo; | ||
1039 | impl Foo { | ||
1040 | fn foo(&self) { | ||
1041 | self.bar()<|>; | ||
1042 | } | ||
1043 | } | ||
1044 | ", | ||
1045 | r" | ||
1046 | struct Foo; | ||
1047 | impl Foo { | ||
1048 | fn foo(&self) { | ||
1049 | self.bar(); | ||
1050 | } | ||
1051 | fn bar(&self) { | ||
1052 | todo!(); | ||
1053 | } | ||
1054 | } | ||
1055 | ", | ||
1056 | ) | ||
1057 | } | ||
1058 | } | ||