diff options
author | Aleksey Kladov <[email protected]> | 2020-07-03 17:15:03 +0100 |
---|---|---|
committer | Aleksey Kladov <[email protected]> | 2020-07-03 18:32:18 +0100 |
commit | 66b6a433c6243b8be72bbd04a40d0a38cedb11b4 (patch) | |
tree | ab31a657463daa426db793f335cb6bfcf7c8e41a /crates/ra_assists/src/handlers/add_function.rs | |
parent | a434ecef51bc8cf20b626267ef90c2887aa5116a (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.rs | 1053 |
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 @@ | |||
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: 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 | // ``` | ||
44 | pub(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 | |||
76 | struct 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 | |||
85 | impl 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 | |||
99 | struct 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 | |||
108 | impl 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 | |||
174 | enum GeneratedFunctionTarget { | ||
175 | BehindItem(SyntaxNode), | ||
176 | InEmptyItemList(ast::ItemList), | ||
177 | } | ||
178 | |||
179 | impl GeneratedFunctionTarget { | ||
180 | fn syntax(&self) -> &SyntaxNode { | ||
181 | match self { | ||
182 | GeneratedFunctionTarget::BehindItem(it) => it, | ||
183 | GeneratedFunctionTarget::InEmptyItemList(it) => it.syntax(), | ||
184 | } | ||
185 | } | ||
186 | } | ||
187 | |||
188 | fn 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 | ||
194 | fn 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 | /// ``` | ||
226 | fn 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 | |||
248 | fn 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 | |||
262 | fn 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 | ||
283 | fn 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 | |||
303 | fn 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)] | ||
328 | mod 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" | ||
338 | fn foo() { | ||
339 | bar<|>(); | ||
340 | } | ||
341 | ", | ||
342 | r" | ||
343 | fn foo() { | ||
344 | bar(); | ||
345 | } | ||
346 | |||
347 | fn 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" | ||
361 | impl Foo { | ||
362 | fn foo() { | ||
363 | bar<|>(); | ||
364 | } | ||
365 | } | ||
366 | ", | ||
367 | r" | ||
368 | impl Foo { | ||
369 | fn foo() { | ||
370 | bar(); | ||
371 | } | ||
372 | } | ||
373 | |||
374 | fn 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" | ||
387 | fn foo1() { | ||
388 | bar<|>(); | ||
389 | } | ||
390 | |||
391 | fn foo2() {} | ||
392 | ", | ||
393 | r" | ||
394 | fn foo1() { | ||
395 | bar(); | ||
396 | } | ||
397 | |||
398 | fn bar() { | ||
399 | ${0:todo!()} | ||
400 | } | ||
401 | |||
402 | fn 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" | ||
412 | mod baz { | ||
413 | fn foo() { | ||
414 | bar<|>(); | ||
415 | } | ||
416 | } | ||
417 | ", | ||
418 | r" | ||
419 | mod 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" | ||
437 | struct Baz; | ||
438 | fn baz() -> Baz { todo!() } | ||
439 | fn foo() { | ||
440 | bar<|>(baz()); | ||
441 | } | ||
442 | ", | ||
443 | r" | ||
444 | struct Baz; | ||
445 | fn baz() -> Baz { todo!() } | ||
446 | fn foo() { | ||
447 | bar(baz()); | ||
448 | } | ||
449 | |||
450 | fn 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" | ||
462 | struct Baz; | ||
463 | impl Baz { | ||
464 | fn foo(&self) -> Baz { | ||
465 | ba<|>r(self.baz()) | ||
466 | } | ||
467 | fn baz(&self) -> Baz { | ||
468 | Baz | ||
469 | } | ||
470 | } | ||
471 | ", | ||
472 | r" | ||
473 | struct Baz; | ||
474 | impl Baz { | ||
475 | fn foo(&self) -> Baz { | ||
476 | bar(self.baz()) | ||
477 | } | ||
478 | fn baz(&self) -> Baz { | ||
479 | Baz | ||
480 | } | ||
481 | } | ||
482 | |||
483 | fn 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#" | ||
495 | fn foo() { | ||
496 | <|>bar("bar") | ||
497 | } | ||
498 | "#, | ||
499 | r#" | ||
500 | fn foo() { | ||
501 | bar("bar") | ||
502 | } | ||
503 | |||
504 | fn 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#" | ||
516 | fn foo() { | ||
517 | <|>bar('x') | ||
518 | } | ||
519 | "#, | ||
520 | r#" | ||
521 | fn foo() { | ||
522 | bar('x') | ||
523 | } | ||
524 | |||
525 | fn 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" | ||
537 | fn foo() { | ||
538 | <|>bar(42) | ||
539 | } | ||
540 | ", | ||
541 | r" | ||
542 | fn foo() { | ||
543 | bar(42) | ||
544 | } | ||
545 | |||
546 | fn 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" | ||
558 | fn foo() { | ||
559 | <|>bar(42 as u8) | ||
560 | } | ||
561 | ", | ||
562 | r" | ||
563 | fn foo() { | ||
564 | bar(42 as u8) | ||
565 | } | ||
566 | |||
567 | fn 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" | ||
581 | fn foo() { | ||
582 | let x = 42; | ||
583 | bar<|>(x as u8) | ||
584 | } | ||
585 | ", | ||
586 | r" | ||
587 | fn foo() { | ||
588 | let x = 42; | ||
589 | bar(x as u8) | ||
590 | } | ||
591 | |||
592 | fn 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" | ||
604 | fn foo() { | ||
605 | let worble = (); | ||
606 | <|>bar(worble) | ||
607 | } | ||
608 | ", | ||
609 | r" | ||
610 | fn foo() { | ||
611 | let worble = (); | ||
612 | bar(worble) | ||
613 | } | ||
614 | |||
615 | fn 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" | ||
627 | trait Foo {} | ||
628 | fn foo() -> impl Foo { | ||
629 | todo!() | ||
630 | } | ||
631 | fn baz() { | ||
632 | <|>bar(foo()) | ||
633 | } | ||
634 | ", | ||
635 | r" | ||
636 | trait Foo {} | ||
637 | fn foo() -> impl Foo { | ||
638 | todo!() | ||
639 | } | ||
640 | fn baz() { | ||
641 | bar(foo()) | ||
642 | } | ||
643 | |||
644 | fn 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" | ||
656 | struct Baz; | ||
657 | fn baz() -> Baz { todo!() } | ||
658 | |||
659 | fn foo() { | ||
660 | bar<|>(&baz()) | ||
661 | } | ||
662 | ", | ||
663 | r" | ||
664 | struct Baz; | ||
665 | fn baz() -> Baz { todo!() } | ||
666 | |||
667 | fn foo() { | ||
668 | bar(&baz()) | ||
669 | } | ||
670 | |||
671 | fn 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" | ||
683 | mod Baz { | ||
684 | pub struct Bof; | ||
685 | pub fn baz() -> Bof { Bof } | ||
686 | } | ||
687 | fn foo() { | ||
688 | <|>bar(Baz::baz()) | ||
689 | } | ||
690 | ", | ||
691 | r" | ||
692 | mod Baz { | ||
693 | pub struct Bof; | ||
694 | pub fn baz() -> Bof { Bof } | ||
695 | } | ||
696 | fn foo() { | ||
697 | bar(Baz::baz()) | ||
698 | } | ||
699 | |||
700 | fn 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" | ||
714 | fn foo<T>(t: T) { | ||
715 | <|>bar(t) | ||
716 | } | ||
717 | ", | ||
718 | r" | ||
719 | fn foo<T>(t: T) { | ||
720 | bar(t) | ||
721 | } | ||
722 | |||
723 | fn 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" | ||
737 | struct Baz; | ||
738 | impl Baz { | ||
739 | fn new() -> Self { Baz } | ||
740 | } | ||
741 | fn foo() { | ||
742 | <|>bar(Baz::new); | ||
743 | } | ||
744 | ", | ||
745 | r" | ||
746 | struct Baz; | ||
747 | impl Baz { | ||
748 | fn new() -> Self { Baz } | ||
749 | } | ||
750 | fn foo() { | ||
751 | bar(Baz::new); | ||
752 | } | ||
753 | |||
754 | fn 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" | ||
768 | fn foo() { | ||
769 | let closure = |x: i64| x - 1; | ||
770 | <|>bar(closure) | ||
771 | } | ||
772 | ", | ||
773 | r" | ||
774 | fn foo() { | ||
775 | let closure = |x: i64| x - 1; | ||
776 | bar(closure) | ||
777 | } | ||
778 | |||
779 | fn 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" | ||
791 | fn foo() { | ||
792 | <|>bar(baz) | ||
793 | } | ||
794 | ", | ||
795 | r" | ||
796 | fn foo() { | ||
797 | bar(baz) | ||
798 | } | ||
799 | |||
800 | fn 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" | ||
812 | struct Baz; | ||
813 | fn baz() -> Baz { Baz } | ||
814 | fn foo() { | ||
815 | <|>bar(baz(), baz()) | ||
816 | } | ||
817 | ", | ||
818 | r" | ||
819 | struct Baz; | ||
820 | fn baz() -> Baz { Baz } | ||
821 | fn foo() { | ||
822 | bar(baz(), baz()) | ||
823 | } | ||
824 | |||
825 | fn 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#" | ||
837 | struct Baz; | ||
838 | fn baz() -> Baz { Baz } | ||
839 | fn foo() { | ||
840 | <|>bar(baz(), baz(), "foo", "bar") | ||
841 | } | ||
842 | "#, | ||
843 | r#" | ||
844 | struct Baz; | ||
845 | fn baz() -> Baz { Baz } | ||
846 | fn foo() { | ||
847 | bar(baz(), baz(), "foo", "bar") | ||
848 | } | ||
849 | |||
850 | fn 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" | ||
862 | mod bar {} | ||
863 | |||
864 | fn foo() { | ||
865 | bar::my_fn<|>() | ||
866 | } | ||
867 | ", | ||
868 | r" | ||
869 | mod bar { | ||
870 | pub(crate) fn my_fn() { | ||
871 | ${0:todo!()} | ||
872 | } | ||
873 | } | ||
874 | |||
875 | fn 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 | " | ||
890 | mod foo { | ||
891 | pub struct Foo; | ||
892 | } | ||
893 | fn bar() { | ||
894 | use foo::Foo; | ||
895 | let foo = Foo; | ||
896 | baz<|>(foo) | ||
897 | } | ||
898 | ", | ||
899 | " | ||
900 | mod foo { | ||
901 | pub struct Foo; | ||
902 | } | ||
903 | fn bar() { | ||
904 | use foo::Foo; | ||
905 | let foo = Foo; | ||
906 | baz(foo) | ||
907 | } | ||
908 | |||
909 | fn 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" | ||
921 | mod bar { | ||
922 | fn something_else() {} | ||
923 | } | ||
924 | |||
925 | fn foo() { | ||
926 | bar::my_fn<|>() | ||
927 | } | ||
928 | ", | ||
929 | r" | ||
930 | mod bar { | ||
931 | fn something_else() {} | ||
932 | |||
933 | pub(crate) fn my_fn() { | ||
934 | ${0:todo!()} | ||
935 | } | ||
936 | } | ||
937 | |||
938 | fn 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" | ||
950 | mod bar { | ||
951 | mod baz {} | ||
952 | } | ||
953 | |||
954 | fn foo() { | ||
955 | bar::baz::my_fn<|>() | ||
956 | } | ||
957 | ", | ||
958 | r" | ||
959 | mod bar { | ||
960 | mod baz { | ||
961 | pub(crate) fn my_fn() { | ||
962 | ${0:todo!()} | ||
963 | } | ||
964 | } | ||
965 | } | ||
966 | |||
967 | fn 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 | ||
980 | mod foo; | ||
981 | |||
982 | fn main() { | ||
983 | foo::bar<|>() | ||
984 | } | ||
985 | //- /foo.rs | ||
986 | ", | ||
987 | r" | ||
988 | |||
989 | |||
990 | pub(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" | ||
1001 | fn foo() { | ||
1002 | bar<|>(); | ||
1003 | } | ||
1004 | |||
1005 | fn 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" | ||
1018 | fn foo() { | ||
1019 | bar(b<|>az); | ||
1020 | } | ||
1021 | |||
1022 | fn 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" | ||
1033 | struct Foo; | ||
1034 | impl Foo { | ||
1035 | fn foo(&self) { | ||
1036 | self.bar()<|>; | ||
1037 | } | ||
1038 | } | ||
1039 | ", | ||
1040 | r" | ||
1041 | struct Foo; | ||
1042 | impl Foo { | ||
1043 | fn foo(&self) { | ||
1044 | self.bar(); | ||
1045 | } | ||
1046 | fn bar(&self) { | ||
1047 | todo!(); | ||
1048 | } | ||
1049 | } | ||
1050 | ", | ||
1051 | ) | ||
1052 | } | ||
1053 | } | ||