diff options
author | Galilée 'Bill' Enguehard <[email protected]> | 2020-05-21 22:27:38 +0100 |
---|---|---|
committer | Galilée 'Bill' Enguehard <[email protected]> | 2020-05-21 22:27:38 +0100 |
commit | 7fece3bdd2450c0807f7dd742239cae95f0cc65e (patch) | |
tree | 866c4db826c959e79c63a6727bdb9f2c61e6fc4f /crates/ra_assists/src/handlers/add_function.rs | |
parent | db926218b2082077750291f8426ddd28b284cd08 (diff) | |
parent | 59732df8d40dfadc6dcf5951265416576399712a (diff) |
Merge branch 'master' of github.com:rust-analyzer/rust-analyzer into modname_spacing
Diffstat (limited to 'crates/ra_assists/src/handlers/add_function.rs')
-rw-r--r-- | crates/ra_assists/src/handlers/add_function.rs | 355 |
1 files changed, 219 insertions, 136 deletions
diff --git a/crates/ra_assists/src/handlers/add_function.rs b/crates/ra_assists/src/handlers/add_function.rs index 6c7456579..24f931a85 100644 --- a/crates/ra_assists/src/handlers/add_function.rs +++ b/crates/ra_assists/src/handlers/add_function.rs | |||
@@ -1,13 +1,21 @@ | |||
1 | use hir::HirDisplay; | ||
2 | use ra_db::FileId; | ||
1 | use ra_syntax::{ | 3 | use ra_syntax::{ |
2 | ast::{self, AstNode}, | 4 | ast::{ |
5 | self, | ||
6 | edit::{AstNodeEdit, IndentLevel}, | ||
7 | make, ArgListOwner, AstNode, ModuleItemOwner, | ||
8 | }, | ||
3 | SyntaxKind, SyntaxNode, TextSize, | 9 | SyntaxKind, SyntaxNode, TextSize, |
4 | }; | 10 | }; |
5 | |||
6 | use crate::{Assist, AssistCtx, AssistFile, AssistId}; | ||
7 | use ast::{edit::IndentLevel, ArgListOwner, ModuleItemOwner}; | ||
8 | use hir::HirDisplay; | ||
9 | use rustc_hash::{FxHashMap, FxHashSet}; | 11 | use rustc_hash::{FxHashMap, FxHashSet}; |
10 | 12 | ||
13 | use crate::{ | ||
14 | assist_config::SnippetCap, | ||
15 | utils::{render_snippet, Cursor}, | ||
16 | AssistContext, AssistId, Assists, | ||
17 | }; | ||
18 | |||
11 | // Assist: add_function | 19 | // Assist: add_function |
12 | // | 20 | // |
13 | // Adds a stub function with a signature matching the function under the cursor. | 21 | // Adds a stub function with a signature matching the function under the cursor. |
@@ -29,11 +37,11 @@ use rustc_hash::{FxHashMap, FxHashSet}; | |||
29 | // } | 37 | // } |
30 | // | 38 | // |
31 | // fn bar(arg: &str, baz: Baz) { | 39 | // fn bar(arg: &str, baz: Baz) { |
32 | // todo!() | 40 | // ${0:todo!()} |
33 | // } | 41 | // } |
34 | // | 42 | // |
35 | // ``` | 43 | // ``` |
36 | pub(crate) fn add_function(ctx: AssistCtx) -> Option<Assist> { | 44 | pub(crate) fn add_function(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
37 | let path_expr: ast::PathExpr = ctx.find_node_at_offset()?; | 45 | let path_expr: ast::PathExpr = ctx.find_node_at_offset()?; |
38 | let call = path_expr.syntax().parent().and_then(ast::CallExpr::cast)?; | 46 | let call = path_expr.syntax().parent().and_then(ast::CallExpr::cast)?; |
39 | let path = path_expr.path()?; | 47 | let path = path_expr.path()?; |
@@ -43,36 +51,49 @@ pub(crate) fn add_function(ctx: AssistCtx) -> Option<Assist> { | |||
43 | return None; | 51 | return None; |
44 | } | 52 | } |
45 | 53 | ||
46 | let target_module = if let Some(qualifier) = path.qualifier() { | 54 | let target_module = match path.qualifier() { |
47 | if let Some(hir::PathResolution::Def(hir::ModuleDef::Module(module))) = | 55 | Some(qualifier) => match ctx.sema.resolve_path(&qualifier) { |
48 | ctx.sema.resolve_path(&qualifier) | 56 | Some(hir::PathResolution::Def(hir::ModuleDef::Module(module))) => Some(module), |
49 | { | 57 | _ => return None, |
50 | Some(module.definition_source(ctx.sema.db)) | 58 | }, |
51 | } else { | 59 | None => None, |
52 | return None; | ||
53 | } | ||
54 | } else { | ||
55 | None | ||
56 | }; | 60 | }; |
57 | 61 | ||
58 | let function_builder = FunctionBuilder::from_call(&ctx, &call, &path, target_module)?; | 62 | let function_builder = FunctionBuilder::from_call(&ctx, &call, &path, target_module)?; |
59 | 63 | ||
60 | ctx.add_assist(AssistId("add_function"), "Add function", |edit| { | 64 | let target = call.syntax().text_range(); |
61 | edit.target(call.syntax().text_range()); | 65 | acc.add(AssistId("add_function"), "Add function", target, |builder| { |
62 | 66 | let function_template = function_builder.render(); | |
63 | if let Some(function_template) = function_builder.render() { | 67 | builder.set_file(function_template.file); |
64 | edit.set_file(function_template.file); | 68 | let new_fn = function_template.to_string(ctx.config.snippet_cap); |
65 | edit.set_cursor(function_template.cursor_offset); | 69 | match ctx.config.snippet_cap { |
66 | edit.insert(function_template.insert_offset, function_template.fn_def.to_string()); | 70 | Some(cap) => builder.insert_snippet(cap, function_template.insert_offset, new_fn), |
71 | None => builder.insert(function_template.insert_offset, new_fn), | ||
67 | } | 72 | } |
68 | }) | 73 | }) |
69 | } | 74 | } |
70 | 75 | ||
71 | struct FunctionTemplate { | 76 | struct FunctionTemplate { |
72 | insert_offset: TextSize, | 77 | insert_offset: TextSize, |
73 | cursor_offset: TextSize, | 78 | placeholder_expr: ast::MacroCall, |
74 | fn_def: ast::SourceFile, | 79 | leading_ws: String, |
75 | file: AssistFile, | 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 | } | ||
76 | } | 97 | } |
77 | 98 | ||
78 | struct FunctionBuilder { | 99 | struct FunctionBuilder { |
@@ -80,68 +101,73 @@ struct FunctionBuilder { | |||
80 | fn_name: ast::Name, | 101 | fn_name: ast::Name, |
81 | type_params: Option<ast::TypeParamList>, | 102 | type_params: Option<ast::TypeParamList>, |
82 | params: ast::ParamList, | 103 | params: ast::ParamList, |
83 | file: AssistFile, | 104 | file: FileId, |
84 | needs_pub: bool, | 105 | needs_pub: bool, |
85 | } | 106 | } |
86 | 107 | ||
87 | impl FunctionBuilder { | 108 | impl FunctionBuilder { |
88 | /// Prepares a generated function that matches `call` in `generate_in` | 109 | /// Prepares a generated function that matches `call`. |
89 | /// (or as close to `call` as possible, if `generate_in` is `None`) | 110 | /// The function is generated in `target_module` or next to `call` |
90 | fn from_call( | 111 | fn from_call( |
91 | ctx: &AssistCtx, | 112 | ctx: &AssistContext, |
92 | call: &ast::CallExpr, | 113 | call: &ast::CallExpr, |
93 | path: &ast::Path, | 114 | path: &ast::Path, |
94 | target_module: Option<hir::InFile<hir::ModuleSource>>, | 115 | target_module: Option<hir::Module>, |
95 | ) -> Option<Self> { | 116 | ) -> Option<Self> { |
96 | let needs_pub = target_module.is_some(); | 117 | let mut file = ctx.frange.file_id; |
97 | let mut file = AssistFile::default(); | 118 | let target = match &target_module { |
98 | let target = if let Some(target_module) = target_module { | 119 | Some(target_module) => { |
99 | let (in_file, target) = next_space_for_fn_in_module(ctx.sema.db, target_module)?; | 120 | let module_source = target_module.definition_source(ctx.db); |
100 | file = in_file; | 121 | let (in_file, target) = next_space_for_fn_in_module(ctx.sema.db, &module_source)?; |
101 | target | 122 | file = in_file; |
102 | } else { | 123 | target |
103 | next_space_for_fn_after_call_site(&call)? | 124 | } |
125 | None => next_space_for_fn_after_call_site(&call)?, | ||
104 | }; | 126 | }; |
127 | let needs_pub = target_module.is_some(); | ||
128 | let target_module = target_module.or_else(|| ctx.sema.scope(target.syntax()).module())?; | ||
105 | let fn_name = fn_name(&path)?; | 129 | let fn_name = fn_name(&path)?; |
106 | let (type_params, params) = fn_args(ctx, &call)?; | 130 | let (type_params, params) = fn_args(ctx, target_module, &call)?; |
131 | |||
107 | Some(Self { target, fn_name, type_params, params, file, needs_pub }) | 132 | Some(Self { target, fn_name, type_params, params, file, needs_pub }) |
108 | } | 133 | } |
109 | 134 | ||
110 | fn render(self) -> Option<FunctionTemplate> { | 135 | fn render(self) -> FunctionTemplate { |
111 | let placeholder_expr = ast::make::expr_todo(); | 136 | let placeholder_expr = make::expr_todo(); |
112 | let fn_body = ast::make::block_expr(vec![], Some(placeholder_expr)); | 137 | let fn_body = make::block_expr(vec![], Some(placeholder_expr)); |
113 | let mut fn_def = ast::make::fn_def(self.fn_name, self.type_params, self.params, fn_body); | 138 | let visibility = if self.needs_pub { Some(make::visibility_pub_crate()) } else { None }; |
114 | if self.needs_pub { | 139 | let mut fn_def = |
115 | fn_def = ast::make::add_pub_crate_modifier(fn_def); | 140 | make::fn_def(visibility, self.fn_name, self.type_params, self.params, fn_body); |
116 | } | 141 | let leading_ws; |
142 | let trailing_ws; | ||
117 | 143 | ||
118 | let (fn_def, insert_offset) = match self.target { | 144 | let insert_offset = match self.target { |
119 | GeneratedFunctionTarget::BehindItem(it) => { | 145 | GeneratedFunctionTarget::BehindItem(it) => { |
120 | let with_leading_blank_line = ast::make::add_leading_newlines(2, fn_def); | 146 | let indent = IndentLevel::from_node(&it); |
121 | let indented = IndentLevel::from_node(&it).increase_indent(with_leading_blank_line); | 147 | leading_ws = format!("\n\n{}", indent); |
122 | (indented, it.text_range().end()) | 148 | fn_def = fn_def.indent(indent); |
149 | trailing_ws = String::new(); | ||
150 | it.text_range().end() | ||
123 | } | 151 | } |
124 | GeneratedFunctionTarget::InEmptyItemList(it) => { | 152 | GeneratedFunctionTarget::InEmptyItemList(it) => { |
125 | let indent_once = IndentLevel(1); | ||
126 | let indent = IndentLevel::from_node(it.syntax()); | 153 | let indent = IndentLevel::from_node(it.syntax()); |
127 | 154 | leading_ws = format!("\n{}", indent + 1); | |
128 | let fn_def = ast::make::add_leading_newlines(1, fn_def); | 155 | fn_def = fn_def.indent(indent + 1); |
129 | let fn_def = indent_once.increase_indent(fn_def); | 156 | trailing_ws = format!("\n{}", indent); |
130 | let fn_def = ast::make::add_trailing_newlines(1, fn_def); | 157 | it.syntax().text_range().start() + TextSize::of('{') |
131 | let fn_def = indent.increase_indent(fn_def); | ||
132 | (fn_def, it.syntax().text_range().start() + TextSize::of('{')) | ||
133 | } | 158 | } |
134 | }; | 159 | }; |
135 | 160 | ||
136 | let cursor_offset_from_fn_start = fn_def | 161 | let placeholder_expr = |
137 | .syntax() | 162 | fn_def.syntax().descendants().find_map(ast::MacroCall::cast).unwrap(); |
138 | .descendants() | 163 | FunctionTemplate { |
139 | .find_map(ast::MacroCall::cast)? | 164 | insert_offset, |
140 | .syntax() | 165 | placeholder_expr, |
141 | .text_range() | 166 | leading_ws, |
142 | .start(); | 167 | fn_def, |
143 | let cursor_offset = insert_offset + cursor_offset_from_fn_start; | 168 | trailing_ws, |
144 | Some(FunctionTemplate { insert_offset, cursor_offset, fn_def, file: self.file }) | 169 | file: self.file, |
170 | } | ||
145 | } | 171 | } |
146 | } | 172 | } |
147 | 173 | ||
@@ -150,32 +176,41 @@ enum GeneratedFunctionTarget { | |||
150 | InEmptyItemList(ast::ItemList), | 176 | InEmptyItemList(ast::ItemList), |
151 | } | 177 | } |
152 | 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 | |||
153 | fn fn_name(call: &ast::Path) -> Option<ast::Name> { | 188 | fn fn_name(call: &ast::Path) -> Option<ast::Name> { |
154 | let name = call.segment()?.syntax().to_string(); | 189 | let name = call.segment()?.syntax().to_string(); |
155 | Some(ast::make::name(&name)) | 190 | Some(make::name(&name)) |
156 | } | 191 | } |
157 | 192 | ||
158 | /// Computes the type variables and arguments required for the generated function | 193 | /// Computes the type variables and arguments required for the generated function |
159 | fn fn_args( | 194 | fn fn_args( |
160 | ctx: &AssistCtx, | 195 | ctx: &AssistContext, |
196 | target_module: hir::Module, | ||
161 | call: &ast::CallExpr, | 197 | call: &ast::CallExpr, |
162 | ) -> Option<(Option<ast::TypeParamList>, ast::ParamList)> { | 198 | ) -> Option<(Option<ast::TypeParamList>, ast::ParamList)> { |
163 | let mut arg_names = Vec::new(); | 199 | let mut arg_names = Vec::new(); |
164 | let mut arg_types = Vec::new(); | 200 | let mut arg_types = Vec::new(); |
165 | for arg in call.arg_list()?.args() { | 201 | for arg in call.arg_list()?.args() { |
166 | let arg_name = match fn_arg_name(&arg) { | 202 | arg_names.push(match fn_arg_name(&arg) { |
167 | Some(name) => name, | 203 | Some(name) => name, |
168 | None => String::from("arg"), | 204 | None => String::from("arg"), |
169 | }; | 205 | }); |
170 | arg_names.push(arg_name); | 206 | arg_types.push(match fn_arg_type(ctx, target_module, &arg) { |
171 | arg_types.push(match fn_arg_type(ctx, &arg) { | ||
172 | Some(ty) => ty, | 207 | Some(ty) => ty, |
173 | None => String::from("()"), | 208 | None => String::from("()"), |
174 | }); | 209 | }); |
175 | } | 210 | } |
176 | deduplicate_arg_names(&mut arg_names); | 211 | deduplicate_arg_names(&mut arg_names); |
177 | let params = arg_names.into_iter().zip(arg_types).map(|(name, ty)| ast::make::param(name, ty)); | 212 | let params = arg_names.into_iter().zip(arg_types).map(|(name, ty)| make::param(name, ty)); |
178 | Some((None, ast::make::param_list(params))) | 213 | Some((None, make::param_list(params))) |
179 | } | 214 | } |
180 | 215 | ||
181 | /// Makes duplicate argument names unique by appending incrementing numbers. | 216 | /// Makes duplicate argument names unique by appending incrementing numbers. |
@@ -224,12 +259,21 @@ fn fn_arg_name(fn_arg: &ast::Expr) -> Option<String> { | |||
224 | } | 259 | } |
225 | } | 260 | } |
226 | 261 | ||
227 | fn fn_arg_type(ctx: &AssistCtx, fn_arg: &ast::Expr) -> Option<String> { | 262 | fn fn_arg_type( |
263 | ctx: &AssistContext, | ||
264 | target_module: hir::Module, | ||
265 | fn_arg: &ast::Expr, | ||
266 | ) -> Option<String> { | ||
228 | let ty = ctx.sema.type_of_expr(fn_arg)?; | 267 | let ty = ctx.sema.type_of_expr(fn_arg)?; |
229 | if ty.is_unknown() { | 268 | if ty.is_unknown() { |
230 | return None; | 269 | return None; |
231 | } | 270 | } |
232 | Some(ty.display(ctx.sema.db).to_string()) | 271 | |
272 | if let Ok(rendered) = ty.display_source_code(ctx.db, target_module.into()) { | ||
273 | Some(rendered) | ||
274 | } else { | ||
275 | None | ||
276 | } | ||
233 | } | 277 | } |
234 | 278 | ||
235 | /// Returns the position inside the current mod or file | 279 | /// Returns the position inside the current mod or file |
@@ -258,11 +302,10 @@ fn next_space_for_fn_after_call_site(expr: &ast::CallExpr) -> Option<GeneratedFu | |||
258 | 302 | ||
259 | fn next_space_for_fn_in_module( | 303 | fn next_space_for_fn_in_module( |
260 | db: &dyn hir::db::AstDatabase, | 304 | db: &dyn hir::db::AstDatabase, |
261 | module: hir::InFile<hir::ModuleSource>, | 305 | module_source: &hir::InFile<hir::ModuleSource>, |
262 | ) -> Option<(AssistFile, GeneratedFunctionTarget)> { | 306 | ) -> Option<(FileId, GeneratedFunctionTarget)> { |
263 | let file = module.file_id.original_file(db); | 307 | let file = module_source.file_id.original_file(db); |
264 | let assist_file = AssistFile::TargetFile(file); | 308 | let assist_item = match &module_source.value { |
265 | let assist_item = match module.value { | ||
266 | hir::ModuleSource::SourceFile(it) => { | 309 | hir::ModuleSource::SourceFile(it) => { |
267 | if let Some(last_item) = it.items().last() { | 310 | if let Some(last_item) = it.items().last() { |
268 | GeneratedFunctionTarget::BehindItem(last_item.syntax().clone()) | 311 | GeneratedFunctionTarget::BehindItem(last_item.syntax().clone()) |
@@ -278,12 +321,12 @@ fn next_space_for_fn_in_module( | |||
278 | } | 321 | } |
279 | } | 322 | } |
280 | }; | 323 | }; |
281 | Some((assist_file, assist_item)) | 324 | Some((file, assist_item)) |
282 | } | 325 | } |
283 | 326 | ||
284 | #[cfg(test)] | 327 | #[cfg(test)] |
285 | mod tests { | 328 | mod tests { |
286 | use crate::helpers::{check_assist, check_assist_not_applicable}; | 329 | use crate::tests::{check_assist, check_assist_not_applicable}; |
287 | 330 | ||
288 | use super::*; | 331 | use super::*; |
289 | 332 | ||
@@ -302,7 +345,7 @@ fn foo() { | |||
302 | } | 345 | } |
303 | 346 | ||
304 | fn bar() { | 347 | fn bar() { |
305 | <|>todo!() | 348 | ${0:todo!()} |
306 | } | 349 | } |
307 | ", | 350 | ", |
308 | ) | 351 | ) |
@@ -329,7 +372,7 @@ impl Foo { | |||
329 | } | 372 | } |
330 | 373 | ||
331 | fn bar() { | 374 | fn bar() { |
332 | <|>todo!() | 375 | ${0:todo!()} |
333 | } | 376 | } |
334 | ", | 377 | ", |
335 | ) | 378 | ) |
@@ -353,7 +396,7 @@ fn foo1() { | |||
353 | } | 396 | } |
354 | 397 | ||
355 | fn bar() { | 398 | fn bar() { |
356 | <|>todo!() | 399 | ${0:todo!()} |
357 | } | 400 | } |
358 | 401 | ||
359 | fn foo2() {} | 402 | fn foo2() {} |
@@ -379,7 +422,7 @@ mod baz { | |||
379 | } | 422 | } |
380 | 423 | ||
381 | fn bar() { | 424 | fn bar() { |
382 | <|>todo!() | 425 | ${0:todo!()} |
383 | } | 426 | } |
384 | } | 427 | } |
385 | ", | 428 | ", |
@@ -405,7 +448,7 @@ fn foo() { | |||
405 | } | 448 | } |
406 | 449 | ||
407 | fn bar(baz: Baz) { | 450 | fn bar(baz: Baz) { |
408 | <|>todo!() | 451 | ${0:todo!()} |
409 | } | 452 | } |
410 | ", | 453 | ", |
411 | ); | 454 | ); |
@@ -438,7 +481,7 @@ impl Baz { | |||
438 | } | 481 | } |
439 | 482 | ||
440 | fn bar(baz: Baz) { | 483 | fn bar(baz: Baz) { |
441 | <|>todo!() | 484 | ${0:todo!()} |
442 | } | 485 | } |
443 | ", | 486 | ", |
444 | ) | 487 | ) |
@@ -459,7 +502,7 @@ fn foo() { | |||
459 | } | 502 | } |
460 | 503 | ||
461 | fn bar(arg: &str) { | 504 | fn bar(arg: &str) { |
462 | <|>todo!() | 505 | ${0:todo!()} |
463 | } | 506 | } |
464 | "#, | 507 | "#, |
465 | ) | 508 | ) |
@@ -480,7 +523,7 @@ fn foo() { | |||
480 | } | 523 | } |
481 | 524 | ||
482 | fn bar(arg: char) { | 525 | fn bar(arg: char) { |
483 | <|>todo!() | 526 | ${0:todo!()} |
484 | } | 527 | } |
485 | "#, | 528 | "#, |
486 | ) | 529 | ) |
@@ -501,7 +544,7 @@ fn foo() { | |||
501 | } | 544 | } |
502 | 545 | ||
503 | fn bar(arg: i32) { | 546 | fn bar(arg: i32) { |
504 | <|>todo!() | 547 | ${0:todo!()} |
505 | } | 548 | } |
506 | ", | 549 | ", |
507 | ) | 550 | ) |
@@ -522,7 +565,7 @@ fn foo() { | |||
522 | } | 565 | } |
523 | 566 | ||
524 | fn bar(arg: u8) { | 567 | fn bar(arg: u8) { |
525 | <|>todo!() | 568 | ${0:todo!()} |
526 | } | 569 | } |
527 | ", | 570 | ", |
528 | ) | 571 | ) |
@@ -547,7 +590,7 @@ fn foo() { | |||
547 | } | 590 | } |
548 | 591 | ||
549 | fn bar(x: u8) { | 592 | fn bar(x: u8) { |
550 | <|>todo!() | 593 | ${0:todo!()} |
551 | } | 594 | } |
552 | ", | 595 | ", |
553 | ) | 596 | ) |
@@ -570,7 +613,7 @@ fn foo() { | |||
570 | } | 613 | } |
571 | 614 | ||
572 | fn bar(worble: ()) { | 615 | fn bar(worble: ()) { |
573 | <|>todo!() | 616 | ${0:todo!()} |
574 | } | 617 | } |
575 | ", | 618 | ", |
576 | ) | 619 | ) |
@@ -599,15 +642,40 @@ fn baz() { | |||
599 | } | 642 | } |
600 | 643 | ||
601 | fn bar(foo: impl Foo) { | 644 | fn bar(foo: impl Foo) { |
602 | <|>todo!() | 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!()} | ||
603 | } | 673 | } |
604 | ", | 674 | ", |
605 | ) | 675 | ) |
606 | } | 676 | } |
607 | 677 | ||
608 | #[test] | 678 | #[test] |
609 | #[ignore] | ||
610 | // FIXME print paths properly to make this test pass | ||
611 | fn add_function_with_qualified_path_arg() { | 679 | fn add_function_with_qualified_path_arg() { |
612 | check_assist( | 680 | check_assist( |
613 | add_function, | 681 | add_function, |
@@ -616,10 +684,8 @@ mod Baz { | |||
616 | pub struct Bof; | 684 | pub struct Bof; |
617 | pub fn baz() -> Bof { Bof } | 685 | pub fn baz() -> Bof { Bof } |
618 | } | 686 | } |
619 | mod Foo { | 687 | fn foo() { |
620 | fn foo() { | 688 | <|>bar(Baz::baz()) |
621 | <|>bar(super::Baz::baz()) | ||
622 | } | ||
623 | } | 689 | } |
624 | ", | 690 | ", |
625 | r" | 691 | r" |
@@ -627,14 +693,12 @@ mod Baz { | |||
627 | pub struct Bof; | 693 | pub struct Bof; |
628 | pub fn baz() -> Bof { Bof } | 694 | pub fn baz() -> Bof { Bof } |
629 | } | 695 | } |
630 | mod Foo { | 696 | fn foo() { |
631 | fn foo() { | 697 | bar(Baz::baz()) |
632 | bar(super::Baz::baz()) | 698 | } |
633 | } | ||
634 | 699 | ||
635 | fn bar(baz: super::Baz::Bof) { | 700 | fn bar(baz: Baz::Bof) { |
636 | <|>todo!() | 701 | ${0:todo!()} |
637 | } | ||
638 | } | 702 | } |
639 | ", | 703 | ", |
640 | ) | 704 | ) |
@@ -657,7 +721,7 @@ fn foo<T>(t: T) { | |||
657 | } | 721 | } |
658 | 722 | ||
659 | fn bar<T>(t: T) { | 723 | fn bar<T>(t: T) { |
660 | <|>todo!() | 724 | ${0:todo!()} |
661 | } | 725 | } |
662 | ", | 726 | ", |
663 | ) | 727 | ) |
@@ -688,7 +752,7 @@ fn foo() { | |||
688 | } | 752 | } |
689 | 753 | ||
690 | fn bar(arg: fn() -> Baz) { | 754 | fn bar(arg: fn() -> Baz) { |
691 | <|>todo!() | 755 | ${0:todo!()} |
692 | } | 756 | } |
693 | ", | 757 | ", |
694 | ) | 758 | ) |
@@ -713,7 +777,7 @@ fn foo() { | |||
713 | } | 777 | } |
714 | 778 | ||
715 | fn bar(closure: impl Fn(i64) -> i64) { | 779 | fn bar(closure: impl Fn(i64) -> i64) { |
716 | <|>todo!() | 780 | ${0:todo!()} |
717 | } | 781 | } |
718 | ", | 782 | ", |
719 | ) | 783 | ) |
@@ -734,7 +798,7 @@ fn foo() { | |||
734 | } | 798 | } |
735 | 799 | ||
736 | fn bar(baz: ()) { | 800 | fn bar(baz: ()) { |
737 | <|>todo!() | 801 | ${0:todo!()} |
738 | } | 802 | } |
739 | ", | 803 | ", |
740 | ) | 804 | ) |
@@ -759,7 +823,7 @@ fn foo() { | |||
759 | } | 823 | } |
760 | 824 | ||
761 | fn bar(baz_1: Baz, baz_2: Baz) { | 825 | fn bar(baz_1: Baz, baz_2: Baz) { |
762 | <|>todo!() | 826 | ${0:todo!()} |
763 | } | 827 | } |
764 | ", | 828 | ", |
765 | ) | 829 | ) |
@@ -784,7 +848,7 @@ fn foo() { | |||
784 | } | 848 | } |
785 | 849 | ||
786 | fn bar(baz_1: Baz, baz_2: Baz, arg_1: &str, arg_2: &str) { | 850 | fn bar(baz_1: Baz, baz_2: Baz, arg_1: &str, arg_2: &str) { |
787 | <|>todo!() | 851 | ${0:todo!()} |
788 | } | 852 | } |
789 | "#, | 853 | "#, |
790 | ) | 854 | ) |
@@ -804,7 +868,7 @@ fn foo() { | |||
804 | r" | 868 | r" |
805 | mod bar { | 869 | mod bar { |
806 | pub(crate) fn my_fn() { | 870 | pub(crate) fn my_fn() { |
807 | <|>todo!() | 871 | ${0:todo!()} |
808 | } | 872 | } |
809 | } | 873 | } |
810 | 874 | ||
@@ -816,6 +880,40 @@ fn foo() { | |||
816 | } | 880 | } |
817 | 881 | ||
818 | #[test] | 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] | ||
819 | fn add_function_in_module_containing_other_items() { | 917 | fn add_function_in_module_containing_other_items() { |
820 | check_assist( | 918 | check_assist( |
821 | add_function, | 919 | add_function, |
@@ -833,7 +931,7 @@ mod bar { | |||
833 | fn something_else() {} | 931 | fn something_else() {} |
834 | 932 | ||
835 | pub(crate) fn my_fn() { | 933 | pub(crate) fn my_fn() { |
836 | <|>todo!() | 934 | ${0:todo!()} |
837 | } | 935 | } |
838 | } | 936 | } |
839 | 937 | ||
@@ -861,7 +959,7 @@ fn foo() { | |||
861 | mod bar { | 959 | mod bar { |
862 | mod baz { | 960 | mod baz { |
863 | pub(crate) fn my_fn() { | 961 | pub(crate) fn my_fn() { |
864 | <|>todo!() | 962 | ${0:todo!()} |
865 | } | 963 | } |
866 | } | 964 | } |
867 | } | 965 | } |
@@ -890,7 +988,7 @@ fn main() { | |||
890 | 988 | ||
891 | 989 | ||
892 | pub(crate) fn bar() { | 990 | pub(crate) fn bar() { |
893 | <|>todo!() | 991 | ${0:todo!()} |
894 | }", | 992 | }", |
895 | ) | 993 | ) |
896 | } | 994 | } |
@@ -927,21 +1025,6 @@ fn bar(baz: ()) {} | |||
927 | } | 1025 | } |
928 | 1026 | ||
929 | #[test] | 1027 | #[test] |
930 | fn add_function_not_applicable_if_function_path_not_singleton() { | ||
931 | // In the future this assist could be extended to generate functions | ||
932 | // if the path is in the same crate (or even the same workspace). | ||
933 | // For the beginning, I think this is fine. | ||
934 | check_assist_not_applicable( | ||
935 | add_function, | ||
936 | r" | ||
937 | fn foo() { | ||
938 | other_crate::bar<|>(); | ||
939 | } | ||
940 | ", | ||
941 | ) | ||
942 | } | ||
943 | |||
944 | #[test] | ||
945 | #[ignore] | 1028 | #[ignore] |
946 | fn create_method_with_no_args() { | 1029 | fn create_method_with_no_args() { |
947 | check_assist( | 1030 | check_assist( |